From 7e4c61a66a335c5befd421a4c19e6bf1a1950f00 Mon Sep 17 00:00:00 2001 From: Monomax Bot Date: Mon, 16 Nov 2020 08:06:24 +0000 Subject: [PATCH 01/91] Upgrade dependencies. --- browser/package-lock.json | 1059 +++++++++++----------- browser/package.json | 16 +- go.mod | 21 +- go.sum | 104 +++ jslib/package-lock.json | 6 +- jslib/package.json | 2 +- package-lock.json | 275 ++++-- package.json | 12 +- web/package-lock.json | 1797 ++++++++++++++++++------------------- web/package.json | 50 +- 10 files changed, 1727 insertions(+), 1615 deletions(-) diff --git a/browser/package-lock.json b/browser/package-lock.json index 6d327ea5..0e7b48b5 100644 --- a/browser/package-lock.json +++ b/browser/package-lock.json @@ -13,38 +13,25 @@ } }, "@babel/compat-data": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.11.0.tgz", - "integrity": "sha512-TPSvJfv73ng0pfnEOh17bYMPQbI95+nGWc71Ss4vZdRBHTDqmM9Z8ZV4rYz8Ks7sfzc95n30k6ODIq5UGnXcYQ==", - "dev": true, - "requires": { - "browserslist": "^4.12.0", - "invariant": "^2.2.4", - "semver": "^5.5.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.12.5.tgz", + "integrity": "sha512-DTsS7cxrsH3by8nqQSpFSyjSfSYl57D6Cf4q8dW3LK83tBKBDCkfcay1nYkXq1nIHXnpX8WMMb/O25HOy3h1zg==", + "dev": true }, "@babel/core": { - "version": "7.11.4", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.11.4.tgz", - "integrity": "sha512-5deljj5HlqRXN+5oJTY7Zs37iH3z3b++KjiKtIsJy1NrjOOVSEaJHEetLBhyu0aQOSNNZ/0IuEAan9GzRuDXHg==", + "version": "7.12.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.3.tgz", + "integrity": "sha512-0qXcZYKZp3/6N2jKYVxZv0aNCsxTSVCiK72DTiTYZAu7sjg73W0/aynWjMbiGd87EQL4WyA8reiJVh92AVla9g==", "dev": true, "requires": { "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.11.4", - "@babel/helper-module-transforms": "^7.11.0", - "@babel/helpers": "^7.10.4", - "@babel/parser": "^7.11.4", + "@babel/generator": "^7.12.1", + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helpers": "^7.12.1", + "@babel/parser": "^7.12.3", "@babel/template": "^7.10.4", - "@babel/traverse": "^7.11.0", - "@babel/types": "^7.11.0", + "@babel/traverse": "^7.12.1", + "@babel/types": "^7.12.1", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.1", @@ -82,9 +69,9 @@ } }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", @@ -102,12 +89,12 @@ } }, "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, "ms": { @@ -125,12 +112,12 @@ } }, "@babel/generator": { - "version": "7.11.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.4.tgz", - "integrity": "sha512-Rn26vueFx0eOoz7iifCN2UHT6rGtnkSGWSoDRIy8jZN3B91PzeSULbswfLoOWuTuAcNwpG/mxy+uCTDnZ9Mp1g==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.5.tgz", + "integrity": "sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A==", "dev": true, "requires": { - "@babel/types": "^7.11.0", + "@babel/types": "^7.12.5", "jsesc": "^2.5.1", "source-map": "^0.5.0" }, @@ -142,9 +129,9 @@ "dev": true }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", @@ -170,9 +157,9 @@ "dev": true }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", @@ -199,9 +186,9 @@ "dev": true }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", @@ -212,15 +199,14 @@ } }, "@babel/helper-compilation-targets": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.10.4.tgz", - "integrity": "sha512-a3rYhlsGV0UHNDvrtOXBg8/OpfV0OKTkxKPzIplS1zpx7CygDcWWxckxZeDd3gzPzC4kUT0A4nVFDK0wGMh4MQ==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.12.5.tgz", + "integrity": "sha512-+qH6NrscMolUlzOYngSBMIOQpKUGPPsc61Bu5W10mg84LxZ7cmvnBHzARKbDoFxVvqqAbj6Tg6N7bSrWSPXMyw==", "dev": true, "requires": { - "@babel/compat-data": "^7.10.4", - "browserslist": "^4.12.0", - "invariant": "^2.2.4", - "levenary": "^1.1.1", + "@babel/compat-data": "^7.12.5", + "@babel/helper-validator-option": "^7.12.1", + "browserslist": "^4.14.5", "semver": "^5.5.0" }, "dependencies": { @@ -233,28 +219,27 @@ } }, "@babel/helper-create-class-features-plugin": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.5.tgz", - "integrity": "sha512-0nkdeijB7VlZoLT3r/mY3bUkw3T8WG/hNw+FATs/6+pG2039IJWjTYL0VTISqsNHMUTEnwbVnc89WIJX9Qed0A==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.12.1.tgz", + "integrity": "sha512-hkL++rWeta/OVOBTRJc9a5Azh5mt5WgZUGAKMD8JM141YsE08K//bp1unBBieO6rUKkIPyUE0USQ30jAy3Sk1w==", "dev": true, "requires": { "@babel/helper-function-name": "^7.10.4", - "@babel/helper-member-expression-to-functions": "^7.10.5", + "@babel/helper-member-expression-to-functions": "^7.12.1", "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-replace-supers": "^7.10.4", + "@babel/helper-replace-supers": "^7.12.1", "@babel/helper-split-export-declaration": "^7.10.4" } }, "@babel/helper-create-regexp-features-plugin": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.4.tgz", - "integrity": "sha512-2/hu58IEPKeoLF45DBwx3XFqsbCXmkdAay4spVr2x0jYgRxrSNp+ePwvSsy9g6YSaNDcKIQVPXk1Ov8S2edk2g==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.12.1.tgz", + "integrity": "sha512-rsZ4LGvFTZnzdNZR5HZdmJVuXK8834R5QkF3WvcnBhrlVtF0HSIUC6zbreL9MgjTywhKokn8RIYRiq99+DLAxA==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.10.4", "@babel/helper-regex": "^7.10.4", - "regexpu-core": "^4.7.0" + "regexpu-core": "^4.7.1" } }, "@babel/helper-define-map": { @@ -275,9 +260,9 @@ "dev": true }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", @@ -288,12 +273,12 @@ } }, "@babel/helper-explode-assignable-expression": { - "version": "7.11.4", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.11.4.tgz", - "integrity": "sha512-ux9hm3zR4WV1Y3xXxXkdG/0gxF9nvI0YVmKVhvK9AfMoaQkemL3sJpXw+Xbz65azo8qJiEz2XVDUpK3KYhH3ZQ==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.12.1.tgz", + "integrity": "sha512-dmUwH8XmlrUpVqgtZ737tK88v07l840z9j3OEhCLwKTkjlvKpfqXVIZ0wpK3aeOxspwGrf/5AP5qLx4rO3w5rA==", "dev": true, "requires": { - "@babel/types": "^7.10.4" + "@babel/types": "^7.12.1" }, "dependencies": { "@babel/helper-validator-identifier": { @@ -303,9 +288,9 @@ "dev": true }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", @@ -333,9 +318,9 @@ "dev": true }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", @@ -361,9 +346,9 @@ "dev": true }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", @@ -389,9 +374,9 @@ "dev": true }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", @@ -402,12 +387,12 @@ } }, "@babel/helper-member-expression-to-functions": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.11.0.tgz", - "integrity": "sha512-JbFlKHFntRV5qKw3YC0CvQnDZ4XMwgzzBbld7Ly4Mj4cbFy3KywcR8NtNctRToMWJOVvLINJv525Gd6wwVEx/Q==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.1.tgz", + "integrity": "sha512-k0CIe3tXUKTRSoEx1LQEPFU9vRQfqHtl+kf8eNnDqb4AUJEy5pz6aIiog+YWtVm2jpggjS1laH68bPsR+KWWPQ==", "dev": true, "requires": { - "@babel/types": "^7.11.0" + "@babel/types": "^7.12.1" }, "dependencies": { "@babel/helper-validator-identifier": { @@ -417,9 +402,9 @@ "dev": true }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", @@ -438,27 +423,29 @@ } }, "@babel/helper-module-transforms": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz", - "integrity": "sha512-02EVu8COMuTRO1TAzdMtpBPbe6aQ1w/8fePD2YgQmxZU4gpNWaL9gK3Jp7dxlkUlUCJOTaSeA+Hrm1BRQwqIhg==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz", + "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.10.4", - "@babel/helper-replace-supers": "^7.10.4", - "@babel/helper-simple-access": "^7.10.4", + "@babel/helper-module-imports": "^7.12.1", + "@babel/helper-replace-supers": "^7.12.1", + "@babel/helper-simple-access": "^7.12.1", "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/helper-validator-identifier": "^7.10.4", "@babel/template": "^7.10.4", - "@babel/types": "^7.11.0", + "@babel/traverse": "^7.12.1", + "@babel/types": "^7.12.1", "lodash": "^4.17.19" }, "dependencies": { "@babel/helper-module-imports": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz", - "integrity": "sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", + "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", "dev": true, "requires": { - "@babel/types": "^7.10.4" + "@babel/types": "^7.12.5" } }, "@babel/helper-validator-identifier": { @@ -468,9 +455,9 @@ "dev": true }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", @@ -496,9 +483,9 @@ "dev": true }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", @@ -524,15 +511,14 @@ } }, "@babel/helper-remap-async-to-generator": { - "version": "7.11.4", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.11.4.tgz", - "integrity": "sha512-tR5vJ/vBa9wFy3m5LLv2faapJLnDFxNWff2SAYkSE4rLUdbp7CdObYFgI7wK4T/Mj4UzpjPwzR8Pzmr5m7MHGA==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.12.1.tgz", + "integrity": "sha512-9d0KQCRM8clMPcDwo8SevNs+/9a8yWVVmaE80FGJcEP8N1qToREmWEGnBn8BUlJhYRFz6fqxeRL1sl5Ogsed7A==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.10.4", "@babel/helper-wrap-function": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.4" + "@babel/types": "^7.12.1" }, "dependencies": { "@babel/helper-validator-identifier": { @@ -542,9 +528,9 @@ "dev": true }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", @@ -555,15 +541,15 @@ } }, "@babel/helper-replace-supers": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz", - "integrity": "sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.5.tgz", + "integrity": "sha512-5YILoed0ZyIpF4gKcpZitEnXEJ9UoDRki1Ey6xz46rxOzfNMAhVIJMoune1hmPVxh40LRv1+oafz7UsWX+vyWA==", "dev": true, "requires": { - "@babel/helper-member-expression-to-functions": "^7.10.4", + "@babel/helper-member-expression-to-functions": "^7.12.1", "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" + "@babel/traverse": "^7.12.5", + "@babel/types": "^7.12.5" }, "dependencies": { "@babel/helper-validator-identifier": { @@ -573,9 +559,9 @@ "dev": true }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", @@ -586,13 +572,12 @@ } }, "@babel/helper-simple-access": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz", - "integrity": "sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz", + "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==", "dev": true, "requires": { - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.4" + "@babel/types": "^7.12.1" }, "dependencies": { "@babel/helper-validator-identifier": { @@ -602,9 +587,9 @@ "dev": true }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", @@ -615,12 +600,12 @@ } }, "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.11.0.tgz", - "integrity": "sha512-0XIdiQln4Elglgjbwo9wuJpL/K7AGCY26kmEt0+pRP0TAj4jjyNq1MjoRvikrTVqKcx4Gysxt4cXvVFXP/JO2Q==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz", + "integrity": "sha512-Mf5AUuhG1/OCChOJ/HcADmvcHM42WJockombn8ATJG3OnyiSxBK/Mm5x78BQWvmtXZKHgbjdGL2kin/HOLlZGA==", "dev": true, "requires": { - "@babel/types": "^7.11.0" + "@babel/types": "^7.12.1" }, "dependencies": { "@babel/helper-validator-identifier": { @@ -630,9 +615,9 @@ "dev": true }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", @@ -658,9 +643,9 @@ "dev": true }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", @@ -675,10 +660,16 @@ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz", "integrity": "sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g==" }, + "@babel/helper-validator-option": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.1.tgz", + "integrity": "sha512-YpJabsXlJVWP0USHjnC/AQDTLlZERbON577YUVO/wLpqyj6HAtVYnWaQaN0iUN+1/tWn3c+uKKXjRut5115Y2A==", + "dev": true + }, "@babel/helper-wrap-function": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.10.4.tgz", - "integrity": "sha512-6py45WvEF0MhiLrdxtRjKjufwLL1/ob2qDJgg5JgNdojBAZSAKnAjkyOCNug6n+OBl4VW76XjvgSFTdaMcW0Ug==", + "version": "7.12.3", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.12.3.tgz", + "integrity": "sha512-Cvb8IuJDln3rs6tzjW3Y8UeelAOdnpB8xtQ4sme2MSZ9wOxrbThporC0y/EtE16VAtoyEfLM404Xr1e0OOp+ow==", "dev": true, "requires": { "@babel/helper-function-name": "^7.10.4", @@ -694,9 +685,9 @@ "dev": true }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", @@ -707,14 +698,14 @@ } }, "@babel/helpers": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.10.4.tgz", - "integrity": "sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.5.tgz", + "integrity": "sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA==", "dev": true, "requires": { "@babel/template": "^7.10.4", - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" + "@babel/traverse": "^7.12.5", + "@babel/types": "^7.12.5" }, "dependencies": { "@babel/helper-validator-identifier": { @@ -724,9 +715,9 @@ "dev": true }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", @@ -747,36 +738,36 @@ } }, "@babel/parser": { - "version": "7.11.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.4.tgz", - "integrity": "sha512-MggwidiH+E9j5Sh8pbrX5sJvMcsqS5o+7iB42M9/k0CD63MjYbdP4nhSh7uB5wnv2/RVzTZFTxzF/kIa5mrCqA==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.5.tgz", + "integrity": "sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ==", "dev": true }, "@babel/plugin-proposal-async-generator-functions": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.5.tgz", - "integrity": "sha512-cNMCVezQbrRGvXJwm9fu/1sJj9bHdGAgKodZdLqOQIpfoH3raqmRPBM17+lh7CzhiKRRBrGtZL9WcjxSoGYUSg==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.12.1.tgz", + "integrity": "sha512-d+/o30tJxFxrA1lhzJqiUcEJdI6jKlNregCv5bASeGf2Q4MXmnwH7viDo7nhx1/ohf09oaH8j1GVYG/e3Yqk6A==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-remap-async-to-generator": "^7.10.4", + "@babel/helper-remap-async-to-generator": "^7.12.1", "@babel/plugin-syntax-async-generators": "^7.8.0" } }, "@babel/plugin-proposal-class-properties": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.10.4.tgz", - "integrity": "sha512-vhwkEROxzcHGNu2mzUC0OFFNXdZ4M23ib8aRRcJSsW8BZK9pQMD7QB7csl97NBbgGZO7ZyHUyKDnxzOaP4IrCg==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.12.1.tgz", + "integrity": "sha512-cKp3dlQsFsEs5CWKnN7BnSHOd0EOW8EKpEjkoz1pO2E5KzIDNV9Ros1b0CnmbVgAGXJubOYVBOGCT1OmJwOI7w==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.10.4", + "@babel/helper-create-class-features-plugin": "^7.12.1", "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-proposal-dynamic-import": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.10.4.tgz", - "integrity": "sha512-up6oID1LeidOOASNXgv/CFbgBqTuKJ0cJjz6An5tWD+NVBNlp3VNSBxv2ZdU7SYl3NxJC7agAQDApZusV6uFwQ==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.12.1.tgz", + "integrity": "sha512-a4rhUSZFuq5W8/OO8H7BL5zspjnc1FLd9hlOxIK/f7qG4a0qsqk8uvF/ywgBA8/OmjsapjpvaEOYItfGG1qIvQ==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4", @@ -784,9 +775,9 @@ } }, "@babel/plugin-proposal-export-namespace-from": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.10.4.tgz", - "integrity": "sha512-aNdf0LY6/3WXkhh0Fdb6Zk9j1NMD8ovj3F6r0+3j837Pn1S1PdNtcwJ5EG9WkVPNHPxyJDaxMaAOVq4eki0qbg==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.12.1.tgz", + "integrity": "sha512-6CThGf0irEkzujYS5LQcjBx8j/4aQGiVv7J9+2f7pGfxqyKh3WnmVJYW3hdrQjyksErMGBPQrCnHfOtna+WLbw==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4", @@ -794,9 +785,9 @@ } }, "@babel/plugin-proposal-json-strings": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.10.4.tgz", - "integrity": "sha512-fCL7QF0Jo83uy1K0P2YXrfX11tj3lkpN7l4dMv9Y9VkowkhkQDwFHFd8IiwyK5MZjE8UpbgokkgtcReH88Abaw==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.12.1.tgz", + "integrity": "sha512-GoLDUi6U9ZLzlSda2Df++VSqDJg3CG+dR0+iWsv6XRw1rEq+zwt4DirM9yrxW6XWaTpmai1cWJLMfM8qQJf+yw==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4", @@ -804,9 +795,9 @@ } }, "@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.11.0.tgz", - "integrity": "sha512-/f8p4z+Auz0Uaf+i8Ekf1iM7wUNLcViFUGiPxKeXvxTSl63B875YPiVdUDdem7hREcI0E0kSpEhS8tF5RphK7Q==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.12.1.tgz", + "integrity": "sha512-k8ZmVv0JU+4gcUGeCDZOGd0lCIamU/sMtIiX3UWnUc5yzgq6YUGyEolNYD+MLYKfSzgECPcqetVcJP9Afe/aCA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4", @@ -814,9 +805,9 @@ } }, "@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.10.4.tgz", - "integrity": "sha512-wq5n1M3ZUlHl9sqT2ok1T2/MTt6AXE0e1Lz4WzWBr95LsAZ5qDXe4KnFuauYyEyLiohvXFMdbsOTMyLZs91Zlw==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.12.1.tgz", + "integrity": "sha512-nZY0ESiaQDI1y96+jk6VxMOaL4LPo/QDHBqL+SF3/vl6dHkTwHlOI8L4ZwuRBHgakRBw5zsVylel7QPbbGuYgg==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4", @@ -824,9 +815,9 @@ } }, "@babel/plugin-proposal-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.10.4.tgz", - "integrity": "sha512-73/G7QoRoeNkLZFxsoCCvlg4ezE4eM+57PnOqgaPOozd5myfj7p0muD1mRVJvbUWbOzD+q3No2bWbaKy+DJ8DA==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.12.5.tgz", + "integrity": "sha512-UiAnkKuOrCyjZ3sYNHlRlfuZJbBHknMQ9VMwVeX97Ofwx7RpD6gS2HfqTCh8KNUQgcOm8IKt103oR4KIjh7Q8g==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4", @@ -834,20 +825,20 @@ } }, "@babel/plugin-proposal-object-rest-spread": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.11.0.tgz", - "integrity": "sha512-wzch41N4yztwoRw0ak+37wxwJM2oiIiy6huGCoqkvSTA9acYWcPfn9Y4aJqmFFJ70KTJUu29f3DQ43uJ9HXzEA==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.1.tgz", + "integrity": "sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4", "@babel/plugin-syntax-object-rest-spread": "^7.8.0", - "@babel/plugin-transform-parameters": "^7.10.4" + "@babel/plugin-transform-parameters": "^7.12.1" } }, "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.10.4.tgz", - "integrity": "sha512-LflT6nPh+GK2MnFiKDyLiqSqVHkQnVf7hdoAvyTnnKj9xB3docGRsdPuxp6qqqW19ifK3xgc9U5/FwrSaCNX5g==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.12.1.tgz", + "integrity": "sha512-hFvIjgprh9mMw5v42sJWLI1lzU5L2sznP805zeT6rySVRA0Y18StRhDqhSxlap0oVgItRsB6WSROp4YnJTJz0g==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4", @@ -855,33 +846,33 @@ } }, "@babel/plugin-proposal-optional-chaining": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.11.0.tgz", - "integrity": "sha512-v9fZIu3Y8562RRwhm1BbMRxtqZNFmFA2EG+pT2diuU8PT3H6T/KXoZ54KgYisfOFZHV6PfvAiBIZ9Rcz+/JCxA==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.12.1.tgz", + "integrity": "sha512-c2uRpY6WzaVDzynVY9liyykS+kVU+WRZPMPYpkelXH8KBt1oXoI89kPbZKKG/jDT5UK92FTW2fZkZaJhdiBabw==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-skip-transparent-expression-wrappers": "^7.11.0", + "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1", "@babel/plugin-syntax-optional-chaining": "^7.8.0" } }, "@babel/plugin-proposal-private-methods": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.10.4.tgz", - "integrity": "sha512-wh5GJleuI8k3emgTg5KkJK6kHNsGEr0uBTDBuQUBJwckk9xs1ez79ioheEVVxMLyPscB0LfkbVHslQqIzWV6Bw==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.12.1.tgz", + "integrity": "sha512-mwZ1phvH7/NHK6Kf8LP7MYDogGV+DKB1mryFOEwx5EBNQrosvIczzZFTUmWaeujd5xT6G1ELYWUz3CutMhjE1w==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.10.4", + "@babel/helper-create-class-features-plugin": "^7.12.1", "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.10.4.tgz", - "integrity": "sha512-H+3fOgPnEXFL9zGYtKQe4IDOPKYlZdF1kqFDQRRb8PK4B8af1vAGK04tF5iQAAsui+mHNBQSAtd2/ndEDe9wuA==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.12.1.tgz", + "integrity": "sha512-MYq+l+PvHuw/rKUz1at/vb6nCnQ2gmJBNaM62z0OgH7B2W1D9pvkpYtlti9bGtizNIU1K3zm4bZF9F91efVY0w==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.10.4", + "@babel/helper-create-regexp-features-plugin": "^7.12.1", "@babel/helper-plugin-utils": "^7.10.4" } }, @@ -895,9 +886,9 @@ } }, "@babel/plugin-syntax-class-properties": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.4.tgz", - "integrity": "sha512-GCSBF7iUle6rNugfURwNmCGG3Z/2+opxAMLs1nND4bhEG5PuxTIggDBoeYYSujAlLtsupzOHYJQgPS3pivwXIA==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.1.tgz", + "integrity": "sha512-U40A76x5gTwmESz+qiqssqmeEsKvcSyvtgktrm0uzcARAmM9I1jR221f6Oq+GmHrcD+LvZDag1UTOTe2fL3TeA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" @@ -985,41 +976,41 @@ } }, "@babel/plugin-syntax-top-level-await": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.10.4.tgz", - "integrity": "sha512-ni1brg4lXEmWyafKr0ccFWkJG0CeMt4WV1oyeBW6EFObF4oOHclbkj5cARxAPQyAQ2UTuplJyK4nfkXIMMFvsQ==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.1.tgz", + "integrity": "sha512-i7ooMZFS+a/Om0crxZodrTzNEPJHZrlMVGMTEpFAj6rYY/bKCddB0Dk/YxfPuYXOopuhKk/e1jV6h+WUU9XN3A==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-arrow-functions": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.10.4.tgz", - "integrity": "sha512-9J/oD1jV0ZCBcgnoFWFq1vJd4msoKb/TCpGNFyyLt0zABdcvgK3aYikZ8HjzB14c26bc7E3Q1yugpwGy2aTPNA==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.12.1.tgz", + "integrity": "sha512-5QB50qyN44fzzz4/qxDPQMBCTHgxg3n0xRBLJUmBlLoU/sFvxVWGZF/ZUfMVDQuJUKXaBhbupxIzIfZ6Fwk/0A==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-async-to-generator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.10.4.tgz", - "integrity": "sha512-F6nREOan7J5UXTLsDsZG3DXmZSVofr2tGNwfdrVwkDWHfQckbQXnXSPfD7iO+c/2HGqycwyLST3DnZ16n+cBJQ==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.12.1.tgz", + "integrity": "sha512-SDtqoEcarK1DFlRJ1hHRY5HvJUj5kX4qmtpMAm2QnhOlyuMC4TMdCRgW6WXpv93rZeYNeLP22y8Aq2dbcDRM1A==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.10.4", + "@babel/helper-module-imports": "^7.12.1", "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-remap-async-to-generator": "^7.10.4" + "@babel/helper-remap-async-to-generator": "^7.12.1" }, "dependencies": { "@babel/helper-module-imports": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz", - "integrity": "sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", + "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", "dev": true, "requires": { - "@babel/types": "^7.10.4" + "@babel/types": "^7.12.5" } }, "@babel/helper-validator-identifier": { @@ -1029,9 +1020,9 @@ "dev": true }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", @@ -1042,27 +1033,27 @@ } }, "@babel/plugin-transform-block-scoped-functions": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.10.4.tgz", - "integrity": "sha512-WzXDarQXYYfjaV1szJvN3AD7rZgZzC1JtjJZ8dMHUyiK8mxPRahynp14zzNjU3VkPqPsO38CzxiWO1c9ARZ8JA==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.12.1.tgz", + "integrity": "sha512-5OpxfuYnSgPalRpo8EWGPzIYf0lHBWORCkj5M0oLBwHdlux9Ri36QqGW3/LR13RSVOAoUUMzoPI/jpE4ABcHoA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-block-scoping": { - "version": "7.11.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.11.1.tgz", - "integrity": "sha512-00dYeDE0EVEHuuM+26+0w/SCL0BH2Qy7LwHuI4Hi4MH5gkC8/AqMN5uWFJIsoXZrAphiMm1iXzBw6L2T+eA0ew==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.1.tgz", + "integrity": "sha512-zJyAC9sZdE60r1nVQHblcfCj29Dh2Y0DOvlMkcqSo0ckqjiCwNiUezUKw+RjOCwGfpLRwnAeQ2XlLpsnGkvv9w==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-classes": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.4.tgz", - "integrity": "sha512-2oZ9qLjt161dn1ZE0Ms66xBncQH4In8Sqw1YWgBUZuGVJJS5c0OFZXL6dP2MRHrkU/eKhWg8CzFJhRQl50rQxA==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.12.1.tgz", + "integrity": "sha512-/74xkA7bVdzQTBeSUhLLJgYIcxw/dpEpCdRDiHgPJ3Mv6uC11UhjpOhl72CgqbBCmt1qtssCyB2xnJm1+PFjog==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.10.4", @@ -1070,52 +1061,52 @@ "@babel/helper-function-name": "^7.10.4", "@babel/helper-optimise-call-expression": "^7.10.4", "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-replace-supers": "^7.10.4", + "@babel/helper-replace-supers": "^7.12.1", "@babel/helper-split-export-declaration": "^7.10.4", "globals": "^11.1.0" } }, "@babel/plugin-transform-computed-properties": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.10.4.tgz", - "integrity": "sha512-JFwVDXcP/hM/TbyzGq3l/XWGut7p46Z3QvqFMXTfk6/09m7xZHJUN9xHfsv7vqqD4YnfI5ueYdSJtXqqBLyjBw==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.12.1.tgz", + "integrity": "sha512-vVUOYpPWB7BkgUWPo4C44mUQHpTZXakEqFjbv8rQMg7TC6S6ZhGZ3otQcRH6u7+adSlE5i0sp63eMC/XGffrzg==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-destructuring": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.10.4.tgz", - "integrity": "sha512-+WmfvyfsyF603iPa6825mq6Qrb7uLjTOsa3XOFzlYcYDHSS4QmpOWOL0NNBY5qMbvrcf3tq0Cw+v4lxswOBpgA==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.12.1.tgz", + "integrity": "sha512-fRMYFKuzi/rSiYb2uRLiUENJOKq4Gnl+6qOv5f8z0TZXg3llUwUhsNNwrwaT/6dUhJTzNpBr+CUvEWBtfNY1cw==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-dotall-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.10.4.tgz", - "integrity": "sha512-ZEAVvUTCMlMFAbASYSVQoxIbHm2OkG2MseW6bV2JjIygOjdVv8tuxrCTzj1+Rynh7ODb8GivUy7dzEXzEhuPaA==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.12.1.tgz", + "integrity": "sha512-B2pXeRKoLszfEW7J4Hg9LoFaWEbr/kzo3teWHmtFCszjRNa/b40f9mfeqZsIDLLt/FjwQ6pz/Gdlwy85xNckBA==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.10.4", + "@babel/helper-create-regexp-features-plugin": "^7.12.1", "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-duplicate-keys": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.10.4.tgz", - "integrity": "sha512-GL0/fJnmgMclHiBTTWXNlYjYsA7rDrtsazHG6mglaGSTh0KsrW04qml+Bbz9FL0LcJIRwBWL5ZqlNHKTkU3xAA==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.12.1.tgz", + "integrity": "sha512-iRght0T0HztAb/CazveUpUQrZY+aGKKaWXMJ4uf9YJtqxSUe09j3wteztCUDRHs+SRAL7yMuFqUsLoAKKzgXjw==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-exponentiation-operator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.10.4.tgz", - "integrity": "sha512-S5HgLVgkBcRdyQAHbKj+7KyuWx8C6t5oETmUuwz1pt3WTWJhsUV0WIIXuVvfXMxl/QQyHKlSCNNtaIamG8fysw==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.12.1.tgz", + "integrity": "sha512-7tqwy2bv48q+c1EHbXK0Zx3KXd2RVQp6OC7PbwFNt/dPTAV3Lu5sWtWuAj8owr5wqtWnqHfl2/mJlUmqkChKug==", "dev": true, "requires": { "@babel/helper-builder-binary-assignment-operator-visitor": "^7.10.4", @@ -1123,18 +1114,18 @@ } }, "@babel/plugin-transform-for-of": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.10.4.tgz", - "integrity": "sha512-ItdQfAzu9AlEqmusA/65TqJ79eRcgGmpPPFvBnGILXZH975G0LNjP1yjHvGgfuCxqrPPueXOPe+FsvxmxKiHHQ==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.12.1.tgz", + "integrity": "sha512-Zaeq10naAsuHo7heQvyV0ptj4dlZJwZgNAtBYBnu5nNKJoW62m0zKcIEyVECrUKErkUkg6ajMy4ZfnVZciSBhg==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-function-name": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.10.4.tgz", - "integrity": "sha512-OcDCq2y5+E0dVD5MagT5X+yTRbcvFjDI2ZVAottGH6tzqjx/LKpgkUepu3hp/u4tZBzxxpNGwLsAvGBvQ2mJzg==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.12.1.tgz", + "integrity": "sha512-JF3UgJUILoFrFMEnOJLJkRHSk6LUSXLmEFsA23aR2O5CSLUxbeUX1IZ1YQ7Sn0aXb601Ncwjx73a+FVqgcljVw==", "dev": true, "requires": { "@babel/helper-function-name": "^7.10.4", @@ -1142,156 +1133,164 @@ } }, "@babel/plugin-transform-literals": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.10.4.tgz", - "integrity": "sha512-Xd/dFSTEVuUWnyZiMu76/InZxLTYilOSr1UlHV+p115Z/Le2Fi1KXkJUYz0b42DfndostYlPub3m8ZTQlMaiqQ==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.12.1.tgz", + "integrity": "sha512-+PxVGA+2Ag6uGgL0A5f+9rklOnnMccwEBzwYFL3EUaKuiyVnUipyXncFcfjSkbimLrODoqki1U9XxZzTvfN7IQ==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-member-expression-literals": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.10.4.tgz", - "integrity": "sha512-0bFOvPyAoTBhtcJLr9VcwZqKmSjFml1iVxvPL0ReomGU53CX53HsM4h2SzckNdkQcHox1bpAqzxBI1Y09LlBSw==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.12.1.tgz", + "integrity": "sha512-1sxePl6z9ad0gFMB9KqmYofk34flq62aqMt9NqliS/7hPEpURUCMbyHXrMPlo282iY7nAvUB1aQd5mg79UD9Jg==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-modules-amd": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.10.5.tgz", - "integrity": "sha512-elm5uruNio7CTLFItVC/rIzKLfQ17+fX7EVz5W0TMgIHFo1zY0Ozzx+lgwhL4plzl8OzVn6Qasx5DeEFyoNiRw==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.12.1.tgz", + "integrity": "sha512-tDW8hMkzad5oDtzsB70HIQQRBiTKrhfgwC/KkJeGsaNFTdWhKNt/BiE8c5yj19XiGyrxpbkOfH87qkNg1YGlOQ==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.10.5", + "@babel/helper-module-transforms": "^7.12.1", "@babel/helper-plugin-utils": "^7.10.4", "babel-plugin-dynamic-import-node": "^2.3.3" } }, "@babel/plugin-transform-modules-commonjs": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.10.4.tgz", - "integrity": "sha512-Xj7Uq5o80HDLlW64rVfDBhao6OX89HKUmb+9vWYaLXBZOma4gA6tw4Ni1O5qVDoZWUV0fxMYA0aYzOawz0l+1w==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.12.1.tgz", + "integrity": "sha512-dY789wq6l0uLY8py9c1B48V8mVL5gZh/+PQ5ZPrylPYsnAvnEMjqsUXkuoDVPeVK+0VyGar+D08107LzDQ6pag==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.10.4", + "@babel/helper-module-transforms": "^7.12.1", "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-simple-access": "^7.10.4", + "@babel/helper-simple-access": "^7.12.1", "babel-plugin-dynamic-import-node": "^2.3.3" } }, "@babel/plugin-transform-modules-systemjs": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.10.5.tgz", - "integrity": "sha512-f4RLO/OL14/FP1AEbcsWMzpbUz6tssRaeQg11RH1BP/XnPpRoVwgeYViMFacnkaw4k4wjRSjn3ip1Uw9TaXuMw==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.12.1.tgz", + "integrity": "sha512-Hn7cVvOavVh8yvW6fLwveFqSnd7rbQN3zJvoPNyNaQSvgfKmDBO9U1YL9+PCXGRlZD9tNdWTy5ACKqMuzyn32Q==", "dev": true, "requires": { "@babel/helper-hoist-variables": "^7.10.4", - "@babel/helper-module-transforms": "^7.10.5", + "@babel/helper-module-transforms": "^7.12.1", "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-validator-identifier": "^7.10.4", "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "dependencies": { + "@babel/helper-validator-identifier": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", + "dev": true + } } }, "@babel/plugin-transform-modules-umd": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.10.4.tgz", - "integrity": "sha512-mohW5q3uAEt8T45YT7Qc5ws6mWgJAaL/8BfWD9Dodo1A3RKWli8wTS+WiQ/knF+tXlPirW/1/MqzzGfCExKECA==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.12.1.tgz", + "integrity": "sha512-aEIubCS0KHKM0zUos5fIoQm+AZUMt1ZvMpqz0/H5qAQ7vWylr9+PLYurT+Ic7ID/bKLd4q8hDovaG3Zch2uz5Q==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.10.4", + "@babel/helper-module-transforms": "^7.12.1", "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.10.4.tgz", - "integrity": "sha512-V6LuOnD31kTkxQPhKiVYzYC/Jgdq53irJC/xBSmqcNcqFGV+PER4l6rU5SH2Vl7bH9mLDHcc0+l9HUOe4RNGKA==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.12.1.tgz", + "integrity": "sha512-tB43uQ62RHcoDp9v2Nsf+dSM8sbNodbEicbQNA53zHz8pWUhsgHSJCGpt7daXxRydjb0KnfmB+ChXOv3oADp1Q==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.10.4" + "@babel/helper-create-regexp-features-plugin": "^7.12.1" } }, "@babel/plugin-transform-new-target": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.10.4.tgz", - "integrity": "sha512-YXwWUDAH/J6dlfwqlWsztI2Puz1NtUAubXhOPLQ5gjR/qmQ5U96DY4FQO8At33JN4XPBhrjB8I4eMmLROjjLjw==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.12.1.tgz", + "integrity": "sha512-+eW/VLcUL5L9IvJH7rT1sT0CzkdUTvPrXC2PXTn/7z7tXLBuKvezYbGdxD5WMRoyvyaujOq2fWoKl869heKjhw==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-object-super": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.10.4.tgz", - "integrity": "sha512-5iTw0JkdRdJvr7sY0vHqTpnruUpTea32JHmq/atIWqsnNussbRzjEDyWep8UNztt1B5IusBYg8Irb0bLbiEBCQ==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.12.1.tgz", + "integrity": "sha512-AvypiGJH9hsquNUn+RXVcBdeE3KHPZexWRdimhuV59cSoOt5kFBmqlByorAeUlGG2CJWd0U+4ZtNKga/TB0cAw==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-replace-supers": "^7.10.4" + "@babel/helper-replace-supers": "^7.12.1" } }, "@babel/plugin-transform-parameters": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.10.5.tgz", - "integrity": "sha512-xPHwUj5RdFV8l1wuYiu5S9fqWGM2DrYc24TMvUiRrPVm+SM3XeqU9BcokQX/kEUe+p2RBwy+yoiR1w/Blq6ubw==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.12.1.tgz", + "integrity": "sha512-xq9C5EQhdPK23ZeCdMxl8bbRnAgHFrw5EOC3KJUsSylZqdkCaFEXxGSBuTSObOpiiHHNyb82es8M1QYgfQGfNg==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.10.4", "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-property-literals": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.10.4.tgz", - "integrity": "sha512-ofsAcKiUxQ8TY4sScgsGeR2vJIsfrzqvFb9GvJ5UdXDzl+MyYCaBj/FGzXuv7qE0aJcjWMILny1epqelnFlz8g==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.12.1.tgz", + "integrity": "sha512-6MTCR/mZ1MQS+AwZLplX4cEySjCpnIF26ToWo942nqn8hXSm7McaHQNeGx/pt7suI1TWOWMfa/NgBhiqSnX0cQ==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-regenerator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.10.4.tgz", - "integrity": "sha512-3thAHwtor39A7C04XucbMg17RcZ3Qppfxr22wYzZNcVIkPHfpM9J0SO8zuCV6SZa265kxBJSrfKTvDCYqBFXGw==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.12.1.tgz", + "integrity": "sha512-gYrHqs5itw6i4PflFX3OdBPMQdPbF4bj2REIUxlMRUFk0/ZOAIpDFuViuxPjUL7YC8UPnf+XG7/utJvqXdPKng==", "dev": true, "requires": { "regenerator-transform": "^0.14.2" } }, "@babel/plugin-transform-reserved-words": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.10.4.tgz", - "integrity": "sha512-hGsw1O6Rew1fkFbDImZIEqA8GoidwTAilwCyWqLBM9f+e/u/sQMQu7uX6dyokfOayRuuVfKOW4O7HvaBWM+JlQ==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.12.1.tgz", + "integrity": "sha512-pOnUfhyPKvZpVyBHhSBoX8vfA09b7r00Pmm1sH+29ae2hMTKVmSp4Ztsr8KBKjLjx17H0eJqaRC3bR2iThM54A==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-shorthand-properties": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.10.4.tgz", - "integrity": "sha512-AC2K/t7o07KeTIxMoHneyX90v3zkm5cjHJEokrPEAGEy3UCp8sLKfnfOIGdZ194fyN4wfX/zZUWT9trJZ0qc+Q==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.12.1.tgz", + "integrity": "sha512-GFZS3c/MhX1OusqB1MZ1ct2xRzX5ppQh2JU1h2Pnfk88HtFTM+TWQqJNfwkmxtPQtb/s1tk87oENfXJlx7rSDw==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-spread": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.11.0.tgz", - "integrity": "sha512-UwQYGOqIdQJe4aWNyS7noqAnN2VbaczPLiEtln+zPowRNlD+79w3oi2TWfYe0eZgd+gjZCbsydN7lzWysDt+gw==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.12.1.tgz", + "integrity": "sha512-vuLp8CP0BE18zVYjsEBZ5xoCecMK6LBMMxYzJnh01rxQRvhNhH1csMMmBfNo5tGpGO+NhdSNW2mzIvBu3K1fng==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-skip-transparent-expression-wrappers": "^7.11.0" + "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1" } }, "@babel/plugin-transform-sticky-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.10.4.tgz", - "integrity": "sha512-Ddy3QZfIbEV0VYcVtFDCjeE4xwVTJWTmUtorAJkn6u/92Z/nWJNV+mILyqHKrUxXYKA2EoCilgoPePymKL4DvQ==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.12.1.tgz", + "integrity": "sha512-CiUgKQ3AGVk7kveIaPEET1jNDhZZEl1RPMWdTBE1799bdz++SwqDHStmxfCtDfBhQgCl38YRiSnrMuUMZIWSUQ==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4", @@ -1299,68 +1298,68 @@ } }, "@babel/plugin-transform-template-literals": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.10.5.tgz", - "integrity": "sha512-V/lnPGIb+KT12OQikDvgSuesRX14ck5FfJXt6+tXhdkJ+Vsd0lDCVtF6jcB4rNClYFzaB2jusZ+lNISDk2mMMw==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.12.1.tgz", + "integrity": "sha512-b4Zx3KHi+taXB1dVRBhVJtEPi9h1THCeKmae2qP0YdUHIFhVjtpqqNfxeVAa1xeHVhAy4SbHxEwx5cltAu5apw==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-typeof-symbol": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.10.4.tgz", - "integrity": "sha512-QqNgYwuuW0y0H+kUE/GWSR45t/ccRhe14Fs/4ZRouNNQsyd4o3PG4OtHiIrepbM2WKUBDAXKCAK/Lk4VhzTaGA==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.1.tgz", + "integrity": "sha512-EPGgpGy+O5Kg5pJFNDKuxt9RdmTgj5sgrus2XVeMp/ZIbOESadgILUbm50SNpghOh3/6yrbsH+NB5+WJTmsA7Q==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-unicode-escapes": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.10.4.tgz", - "integrity": "sha512-y5XJ9waMti2J+e7ij20e+aH+fho7Wb7W8rNuu72aKRwCHFqQdhkdU2lo3uZ9tQuboEJcUFayXdARhcxLQ3+6Fg==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.1.tgz", + "integrity": "sha512-I8gNHJLIc7GdApm7wkVnStWssPNbSRMPtgHdmH3sRM1zopz09UWPS4x5V4n1yz/MIWTVnJ9sp6IkuXdWM4w+2Q==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-unicode-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.10.4.tgz", - "integrity": "sha512-wNfsc4s8N2qnIwpO/WP2ZiSyjfpTamT2C9V9FDH/Ljub9zw6P3SjkXcFmc0RQUt96k2fmIvtla2MMjgTwIAC+A==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.12.1.tgz", + "integrity": "sha512-SqH4ClNngh/zGwHZOOQMTD+e8FGWexILV+ePMyiDJttAWRh5dhDL8rcl5lSgU3Huiq6Zn6pWTMvdPAb21Dwdyg==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.10.4", + "@babel/helper-create-regexp-features-plugin": "^7.12.1", "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/preset-env": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.11.0.tgz", - "integrity": "sha512-2u1/k7rG/gTh02dylX2kL3S0IJNF+J6bfDSp4DI2Ma8QN6Y9x9pmAax59fsCk6QUQG0yqH47yJWA+u1I1LccAg==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.12.1.tgz", + "integrity": "sha512-H8kxXmtPaAGT7TyBvSSkoSTUK6RHh61So05SyEbpmr0MCZrsNYn7mGMzzeYoOUCdHzww61k8XBft2TaES+xPLg==", "dev": true, "requires": { - "@babel/compat-data": "^7.11.0", - "@babel/helper-compilation-targets": "^7.10.4", - "@babel/helper-module-imports": "^7.10.4", + "@babel/compat-data": "^7.12.1", + "@babel/helper-compilation-targets": "^7.12.1", + "@babel/helper-module-imports": "^7.12.1", "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-proposal-async-generator-functions": "^7.10.4", - "@babel/plugin-proposal-class-properties": "^7.10.4", - "@babel/plugin-proposal-dynamic-import": "^7.10.4", - "@babel/plugin-proposal-export-namespace-from": "^7.10.4", - "@babel/plugin-proposal-json-strings": "^7.10.4", - "@babel/plugin-proposal-logical-assignment-operators": "^7.11.0", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.10.4", - "@babel/plugin-proposal-numeric-separator": "^7.10.4", - "@babel/plugin-proposal-object-rest-spread": "^7.11.0", - "@babel/plugin-proposal-optional-catch-binding": "^7.10.4", - "@babel/plugin-proposal-optional-chaining": "^7.11.0", - "@babel/plugin-proposal-private-methods": "^7.10.4", - "@babel/plugin-proposal-unicode-property-regex": "^7.10.4", + "@babel/helper-validator-option": "^7.12.1", + "@babel/plugin-proposal-async-generator-functions": "^7.12.1", + "@babel/plugin-proposal-class-properties": "^7.12.1", + "@babel/plugin-proposal-dynamic-import": "^7.12.1", + "@babel/plugin-proposal-export-namespace-from": "^7.12.1", + "@babel/plugin-proposal-json-strings": "^7.12.1", + "@babel/plugin-proposal-logical-assignment-operators": "^7.12.1", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.12.1", + "@babel/plugin-proposal-numeric-separator": "^7.12.1", + "@babel/plugin-proposal-object-rest-spread": "^7.12.1", + "@babel/plugin-proposal-optional-catch-binding": "^7.12.1", + "@babel/plugin-proposal-optional-chaining": "^7.12.1", + "@babel/plugin-proposal-private-methods": "^7.12.1", + "@babel/plugin-proposal-unicode-property-regex": "^7.12.1", "@babel/plugin-syntax-async-generators": "^7.8.0", - "@babel/plugin-syntax-class-properties": "^7.10.4", + "@babel/plugin-syntax-class-properties": "^7.12.1", "@babel/plugin-syntax-dynamic-import": "^7.8.0", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", "@babel/plugin-syntax-json-strings": "^7.8.0", @@ -1370,55 +1369,52 @@ "@babel/plugin-syntax-object-rest-spread": "^7.8.0", "@babel/plugin-syntax-optional-catch-binding": "^7.8.0", "@babel/plugin-syntax-optional-chaining": "^7.8.0", - "@babel/plugin-syntax-top-level-await": "^7.10.4", - "@babel/plugin-transform-arrow-functions": "^7.10.4", - "@babel/plugin-transform-async-to-generator": "^7.10.4", - "@babel/plugin-transform-block-scoped-functions": "^7.10.4", - "@babel/plugin-transform-block-scoping": "^7.10.4", - "@babel/plugin-transform-classes": "^7.10.4", - "@babel/plugin-transform-computed-properties": "^7.10.4", - "@babel/plugin-transform-destructuring": "^7.10.4", - "@babel/plugin-transform-dotall-regex": "^7.10.4", - "@babel/plugin-transform-duplicate-keys": "^7.10.4", - "@babel/plugin-transform-exponentiation-operator": "^7.10.4", - "@babel/plugin-transform-for-of": "^7.10.4", - "@babel/plugin-transform-function-name": "^7.10.4", - "@babel/plugin-transform-literals": "^7.10.4", - "@babel/plugin-transform-member-expression-literals": "^7.10.4", - "@babel/plugin-transform-modules-amd": "^7.10.4", - "@babel/plugin-transform-modules-commonjs": "^7.10.4", - "@babel/plugin-transform-modules-systemjs": "^7.10.4", - "@babel/plugin-transform-modules-umd": "^7.10.4", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.10.4", - "@babel/plugin-transform-new-target": "^7.10.4", - "@babel/plugin-transform-object-super": "^7.10.4", - "@babel/plugin-transform-parameters": "^7.10.4", - "@babel/plugin-transform-property-literals": "^7.10.4", - "@babel/plugin-transform-regenerator": "^7.10.4", - "@babel/plugin-transform-reserved-words": "^7.10.4", - "@babel/plugin-transform-shorthand-properties": "^7.10.4", - "@babel/plugin-transform-spread": "^7.11.0", - "@babel/plugin-transform-sticky-regex": "^7.10.4", - "@babel/plugin-transform-template-literals": "^7.10.4", - "@babel/plugin-transform-typeof-symbol": "^7.10.4", - "@babel/plugin-transform-unicode-escapes": "^7.10.4", - "@babel/plugin-transform-unicode-regex": "^7.10.4", + "@babel/plugin-syntax-top-level-await": "^7.12.1", + "@babel/plugin-transform-arrow-functions": "^7.12.1", + "@babel/plugin-transform-async-to-generator": "^7.12.1", + "@babel/plugin-transform-block-scoped-functions": "^7.12.1", + "@babel/plugin-transform-block-scoping": "^7.12.1", + "@babel/plugin-transform-classes": "^7.12.1", + "@babel/plugin-transform-computed-properties": "^7.12.1", + "@babel/plugin-transform-destructuring": "^7.12.1", + "@babel/plugin-transform-dotall-regex": "^7.12.1", + "@babel/plugin-transform-duplicate-keys": "^7.12.1", + "@babel/plugin-transform-exponentiation-operator": "^7.12.1", + "@babel/plugin-transform-for-of": "^7.12.1", + "@babel/plugin-transform-function-name": "^7.12.1", + "@babel/plugin-transform-literals": "^7.12.1", + "@babel/plugin-transform-member-expression-literals": "^7.12.1", + "@babel/plugin-transform-modules-amd": "^7.12.1", + "@babel/plugin-transform-modules-commonjs": "^7.12.1", + "@babel/plugin-transform-modules-systemjs": "^7.12.1", + "@babel/plugin-transform-modules-umd": "^7.12.1", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.12.1", + "@babel/plugin-transform-new-target": "^7.12.1", + "@babel/plugin-transform-object-super": "^7.12.1", + "@babel/plugin-transform-parameters": "^7.12.1", + "@babel/plugin-transform-property-literals": "^7.12.1", + "@babel/plugin-transform-regenerator": "^7.12.1", + "@babel/plugin-transform-reserved-words": "^7.12.1", + "@babel/plugin-transform-shorthand-properties": "^7.12.1", + "@babel/plugin-transform-spread": "^7.12.1", + "@babel/plugin-transform-sticky-regex": "^7.12.1", + "@babel/plugin-transform-template-literals": "^7.12.1", + "@babel/plugin-transform-typeof-symbol": "^7.12.1", + "@babel/plugin-transform-unicode-escapes": "^7.12.1", + "@babel/plugin-transform-unicode-regex": "^7.12.1", "@babel/preset-modules": "^0.1.3", - "@babel/types": "^7.11.0", - "browserslist": "^4.12.0", + "@babel/types": "^7.12.1", "core-js-compat": "^3.6.2", - "invariant": "^2.2.2", - "levenary": "^1.1.1", "semver": "^5.5.0" }, "dependencies": { "@babel/helper-module-imports": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz", - "integrity": "sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", + "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", "dev": true, "requires": { - "@babel/types": "^7.10.4" + "@babel/types": "^7.12.5" } }, "@babel/helper-validator-identifier": { @@ -1428,9 +1424,9 @@ "dev": true }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", @@ -1447,9 +1443,9 @@ } }, "@babel/preset-modules": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.3.tgz", - "integrity": "sha512-Ra3JXOHBq2xd56xSF7lMKXdjBn3T772Y1Wet3yWnkDly9zHvJki029tAFzvAAK5cf4YV3yoxuP61crYRol6SVg==", + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.4.tgz", + "integrity": "sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0", @@ -1505,9 +1501,9 @@ } }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", @@ -1518,17 +1514,17 @@ } }, "@babel/traverse": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.0.tgz", - "integrity": "sha512-ZB2V+LskoWKNpMq6E5UUCrjtDUh5IOTAyIl0dTjIEoXum/iKWkoIEKIRDnUucO6f+2FzNkE0oD4RLKoPIufDtg==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.5.tgz", + "integrity": "sha512-xa15FbQnias7z9a62LwYAA5SZZPkHIXpd42C6uW68o8uTuua96FHZy1y61Va5P/i83FAAcMpW8+A/QayntzuqA==", "dev": true, "requires": { "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.11.0", + "@babel/generator": "^7.12.5", "@babel/helper-function-name": "^7.10.4", "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/parser": "^7.11.0", - "@babel/types": "^7.11.0", + "@babel/parser": "^7.12.5", + "@babel/types": "^7.12.5", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.19" @@ -1561,9 +1557,9 @@ } }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", @@ -1572,12 +1568,12 @@ } }, "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, "ms": { @@ -1762,9 +1758,9 @@ "optional": true }, "@types/react": { - "version": "16.9.46", - "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.46.tgz", - "integrity": "sha512-dbHzO3aAq1lB3jRQuNpuZ/mnu+CdD3H0WVaaBQA8LTT3S33xhVBUj232T8M3tAhSWJs/D/UqORYUlJNl/8VQZg==", + "version": "16.9.56", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.56.tgz", + "integrity": "sha512-gIkl4J44G/qxbuC6r2Xh+D3CGZpJ+NdWTItAPmZbR5mUS+JQ8Zvzpl0ea5qT/ZT3ZNTUcDKUVqV3xBE8wv/DyQ==", "dev": true, "requires": { "@types/prop-types": "*", @@ -1772,17 +1768,17 @@ }, "dependencies": { "csstype": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.2.tgz", - "integrity": "sha512-ofovWglpqoqbfLNOTBNZLSbMuGrblAf1efvvArGKOZMBrIoJeu5UsAipQolkijtyQx5MtAzT/J9IHj/CEY1mJw==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.5.tgz", + "integrity": "sha512-uVDi8LpBUKQj6sdxNaTetL6FpeCqTjOvAQuQUa/qAqq8oOd4ivkbhgnqayl0dnPal8Tb/yB1tF+gOvCBiicaiQ==", "dev": true } } }, "@types/react-dom": { - "version": "16.9.8", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.8.tgz", - "integrity": "sha512-ykkPQ+5nFknnlU6lDd947WbQ6TE3NNzbQAkInC2EKY1qeYdTKp7onFusmYZb+ityzx2YviqT6BXSu+LyWWJwcA==", + "version": "16.9.9", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.9.tgz", + "integrity": "sha512-jE16FNWO3Logq/Lf+yvEAjKzhpST/Eac8EMd1i4dgZdMczfgqC8EjpxwNgEe3SExHYLliabXDh9DEhhqnlXJhg==", "dev": true, "requires": { "@types/react": "*" @@ -1976,9 +1972,9 @@ "dev": true }, "acorn": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", - "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==", + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", "dev": true }, "aggregate-error": { @@ -2000,9 +1996,9 @@ } }, "ajv": { - "version": "6.12.4", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz", - "integrity": "sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==", + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -2758,9 +2754,9 @@ } }, "bl": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", - "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz", + "integrity": "sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==", "dev": true, "optional": true, "requires": { @@ -2882,21 +2878,13 @@ } }, "browserify-rsa": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", - "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", + "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", "dev": true, "requires": { - "bn.js": "^4.1.0", + "bn.js": "^5.0.0", "randombytes": "^2.0.1" - }, - "dependencies": { - "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true - } } }, "browserify-sign": { @@ -2951,15 +2939,16 @@ } }, "browserslist": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.0.tgz", - "integrity": "sha512-pUsXKAF2lVwhmtpeA3LJrZ76jXuusrNyhduuQs7CDFf9foT4Y38aQOserd2lMe5DSSrjf3fx34oHwryuvxAUgQ==", + "version": "4.14.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.7.tgz", + "integrity": "sha512-BSVRLCeG3Xt/j/1cCGj1019Wbty0H+Yvu2AOuZSuoaUWn3RatbL33Cxk+Q4jRMRAbOm0p7SLravLjpnT6s0vzQ==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001111", - "electron-to-chromium": "^1.3.523", - "escalade": "^3.0.2", - "node-releases": "^1.1.60" + "caniuse-lite": "^1.0.30001157", + "colorette": "^1.2.1", + "electron-to-chromium": "^1.3.591", + "escalade": "^3.1.1", + "node-releases": "^1.1.66" } }, "buffer": { @@ -3174,9 +3163,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001117", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001117.tgz", - "integrity": "sha512-4tY0Fatzdx59kYjQs+bNxUwZB03ZEBgVmJ1UkFPz/Q8OLiUUbjct2EdpnXj0fvFTPej2EkbPIG0w8BWsjAyk1Q==", + "version": "1.0.30001158", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001158.tgz", + "integrity": "sha512-s5loVYY+yKpuVA3HyW8BarzrtJvwHReuzugQXlv1iR3LKSReoFXRm86mT6hT7PEF5RxW+XQZg+6nYjlywYzQ+g==", "dev": true }, "caw": { @@ -3406,6 +3395,12 @@ "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", "dev": true }, + "colorette": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.1.tgz", + "integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==", + "dev": true + }, "commander": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", @@ -3695,12 +3690,12 @@ } }, "core-js-compat": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.5.tgz", - "integrity": "sha512-7ItTKOhOZbznhXAQ2g/slGg1PJV5zDO/WdkTwi7UEOJmkvsE32PWvx6mKtDjiMpjnR2CNf6BAD6sSxIlv7ptng==", + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.7.0.tgz", + "integrity": "sha512-V8yBI3+ZLDVomoWICO6kq/CD28Y4r1M7CWeO4AGpMdMfseu8bkSubBmUPySMGKRTS+su4XQ07zUkAsiu9FCWTg==", "dev": true, "requires": { - "browserslist": "^4.8.5", + "browserslist": "^4.14.6", "semver": "7.0.0" }, "dependencies": { @@ -4388,9 +4383,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.3.544", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.544.tgz", - "integrity": "sha512-jx6H7M1db76Q/dI3MadZC4qwNTvpiq8tdYEJswxexrIm5bH+LKRdg+VAteMF1tJJbBLrcuogE9N3nxT3Dp1gag==", + "version": "1.3.596", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.596.tgz", + "integrity": "sha512-nLO2Wd2yU42eSoNJVQKNf89CcEGqeFZd++QsnN2XIgje1s/19AgctfjLIbPORlvcCO8sYjLwX4iUgDdusOY8Sg==", "dev": true }, "elliptic": { @@ -4574,9 +4569,9 @@ } }, "escalade": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.0.2.tgz", - "integrity": "sha512-gPYAU37hYCUhW5euPeR+Y74F7BL+IBsV93j5cvGriSaD1aG6MGsqsV1yamRdrWrb2j3aiZvb0X+UBOWpx3JWtQ==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "dev": true }, "escape-string-regexp": { @@ -4602,12 +4597,20 @@ "optional": true }, "esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "requires": { - "estraverse": "^4.1.0" + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } } }, "estraverse": { @@ -5713,9 +5716,9 @@ "dev": true }, "gensync": { - "version": "1.0.0-beta.1", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", - "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true }, "get-caller-file": { @@ -6680,15 +6683,6 @@ "p-is-promise": "^1.1.0" } }, - "invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dev": true, - "requires": { - "loose-envify": "^1.0.0" - } - }, "invert-kv": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", @@ -7221,21 +7215,6 @@ "flush-write-stream": "^1.0.2" } }, - "leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true - }, - "levenary": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/levenary/-/levenary-1.1.1.tgz", - "integrity": "sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ==", - "dev": true, - "requires": { - "leven": "^3.1.0" - } - }, "liftoff": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-3.1.0.tgz", @@ -7811,9 +7790,9 @@ } }, "node-releases": { - "version": "1.1.60", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.60.tgz", - "integrity": "sha512-gsO4vjEdQaTusZAEebUWp2a5d7dF5DYoIpDG7WySnk7BuZDW+GPpHXoXXuYawRBr/9t5q54tirPz79kFIWg4dA==", + "version": "1.1.66", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.66.tgz", + "integrity": "sha512-JHEQ1iWPGK+38VLB2H9ef2otU4l8s3yAMt9Xf934r6+ojCYDMHPMqvCc9TnzfeFSP1QEOeU6YZEd3+De0LTCgg==", "dev": true }, "normalize-package-data": { @@ -8637,9 +8616,9 @@ } }, "react": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react/-/react-16.13.1.tgz", - "integrity": "sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==", + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", + "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==", "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", @@ -8647,9 +8626,9 @@ } }, "react-dom": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.13.1.tgz", - "integrity": "sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag==", + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz", + "integrity": "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==", "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", @@ -8671,15 +8650,25 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "react-redux": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.1.tgz", - "integrity": "sha512-T+VfD/bvgGTUA74iW9d2i5THrDQWbweXP0AVNI8tNd1Rk5ch1rnMiJkDD67ejw7YBKM4+REvcvqRuWJb7BLuEg==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.2.tgz", + "integrity": "sha512-8+CQ1EvIVFkYL/vu6Olo7JFLWop1qRUeb46sGtIMDCSpgwPQq8fPLpirIB0iTqFe9XYEFPHssdX8/UwN6pAkEA==", "requires": { - "@babel/runtime": "^7.5.5", - "hoist-non-react-statics": "^3.3.0", + "@babel/runtime": "^7.12.1", + "hoist-non-react-statics": "^3.3.2", "loose-envify": "^1.4.0", "prop-types": "^15.7.2", - "react-is": "^16.9.0" + "react-is": "^16.13.1" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.5.tgz", + "integrity": "sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + } } }, "react-select": { @@ -8834,9 +8823,9 @@ "integrity": "sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw==" }, "regenerate": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.1.tgz", - "integrity": "sha512-j2+C8+NtXQgEKWk49MMP5P/u2GhnahTtVkRIHr5R5lVRlbKvmQ+oS+A5aLKWp2ma5VkT8sh6v+v4hbH0YHR66A==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", "dev": true }, "regenerate-unicode-properties": { @@ -8873,9 +8862,9 @@ } }, "regexpu-core": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.0.tgz", - "integrity": "sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ==", + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.1.tgz", + "integrity": "sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ==", "dev": true, "requires": { "regenerate": "^1.4.0", @@ -10010,9 +9999,9 @@ "optional": true }, "timers-browserify": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.11.tgz", - "integrity": "sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ==", + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", + "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", "dev": true, "requires": { "setimmediate": "^1.0.4" @@ -10417,9 +10406,9 @@ "dev": true }, "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", + "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", "dev": true, "requires": { "punycode": "^2.1.0" @@ -10597,15 +10586,15 @@ "dev": true }, "watchpack": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.4.tgz", - "integrity": "sha512-aWAgTW4MoSJzZPAicljkO1hsi1oKj/RRq/OJQh2PKI2UKL04c2Bs+MBOB+BBABHTXJpf9mCwHN7ANCvYsvY2sg==", + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz", + "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==", "dev": true, "requires": { "chokidar": "^3.4.1", "graceful-fs": "^4.1.2", "neo-async": "^2.5.0", - "watchpack-chokidar2": "^2.0.0" + "watchpack-chokidar2": "^2.0.1" }, "dependencies": { "anymatch": { @@ -10637,9 +10626,9 @@ } }, "chokidar": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.2.tgz", - "integrity": "sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz", + "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==", "dev": true, "optional": true, "requires": { @@ -10650,7 +10639,7 @@ "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.4.0" + "readdirp": "~3.5.0" } }, "fill-range": { @@ -10715,9 +10704,9 @@ "optional": true }, "readdirp": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", - "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", "dev": true, "optional": true, "requires": { @@ -10746,9 +10735,9 @@ } }, "watchpack-chokidar2": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.0.tgz", - "integrity": "sha512-9TyfOyN/zLUbA288wZ8IsMZ+6cbzvsNyEzSBp6e/zkifi6xxbl8SmQ/CxQq32k8NNqrdVEVUVSEf56L4rQ/ZxA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz", + "integrity": "sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==", "dev": true, "optional": true, "requires": { @@ -10756,9 +10745,9 @@ } }, "webpack": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.44.1.tgz", - "integrity": "sha512-4UOGAohv/VGUNQJstzEywwNxqX417FnjZgZJpJQegddzPmTvph37eBIRbRTfdySXzVtJXLJfbMN3mMYhM6GdmQ==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.44.2.tgz", + "integrity": "sha512-6KJVGlCxYdISyurpQ0IPTklv+DULv05rs2hseIXer6D7KrUicRDLFb4IUM1S6LUAKypPM/nSiVSuv8jHu1m3/Q==", "dev": true, "requires": { "@webassemblyjs/ast": "1.9.0", diff --git a/browser/package.json b/browser/package.json index c69cd7e0..5f5be46a 100644 --- a/browser/package.json +++ b/browser/package.json @@ -20,19 +20,19 @@ "classnames": "^2.2.5", "lodash": "^4.17.20", "qs": "^6.9.4", - "react": "^16.12.0", - "react-dom": "^16.12.0", - "react-redux": "^7.2.1", + "react": "^16.14.0", + "react-dom": "^16.14.0", + "react-redux": "^7.2.2", "react-select": "^3.1.0", "redux": "^4.0.4", "redux-logger": "^3.0.6", "redux-thunk": "^2.2.0" }, "devDependencies": { - "@babel/core": "^7.11.4", - "@babel/preset-env": "^7.11.0", - "@types/react": "^16.9.46", - "@types/react-dom": "^16.9.8", + "@babel/core": "^7.12.3", + "@babel/preset-env": "^7.12.1", + "@types/react": "^16.9.56", + "@types/react-dom": "^16.9.9", "concurrently": "^5.3.0", "del": "^5.0.0", "gulp": "^4.0.0", @@ -44,7 +44,7 @@ "prettier": "^1.19.1", "ts-loader": "^6.2.2", "typescript": "^3.9.7", - "webpack": "^4.44.1", + "webpack": "^4.44.2", "webpack-cli": "^3.3.12" } } diff --git a/go.mod b/go.mod index 6f23ece3..8d9a52b0 100644 --- a/go.mod +++ b/go.mod @@ -3,35 +3,36 @@ module github.com/dnote/dnote go 1.13 require ( + github.com/PuerkitoBio/goquery v1.6.0 // indirect github.com/andybalholm/cascadia v1.2.0 // indirect github.com/aymerick/douceur v0.2.0 github.com/dnote/actions v0.2.0 github.com/dnote/color v1.7.0 - github.com/gobuffalo/packr/v2 v2.8.0 - github.com/google/go-cmp v0.5.1 + github.com/gobuffalo/packr/v2 v2.8.1 + github.com/google/go-cmp v0.5.3 github.com/google/go-github v17.0.0+incompatible github.com/google/go-querystring v1.0.0 // indirect - github.com/google/uuid v1.1.1 + github.com/google/uuid v1.1.2 github.com/gorilla/css v1.0.0 // indirect github.com/gorilla/mux v1.8.0 github.com/jinzhu/gorm v1.9.16 github.com/joho/godotenv v1.3.0 github.com/karrick/godirwalk v1.16.1 // indirect + github.com/konsorten/go-windows-terminal-sequences v1.0.3 // indirect github.com/lib/pq v1.8.0 - github.com/mattn/go-colorable v0.1.7 // indirect + github.com/mattn/go-colorable v0.1.8 // indirect github.com/mattn/go-sqlite3 v2.0.3+incompatible github.com/pkg/errors v0.9.1 github.com/radovskyb/watcher v1.0.7 github.com/robfig/cron v1.2.0 github.com/rubenv/sql-migrate v0.0.0-20200616145509-8d140a17f351 github.com/sergi/go-diff v1.1.0 - github.com/sirupsen/logrus v1.6.0 // indirect - github.com/spf13/cobra v1.0.0 - github.com/spf13/pflag v1.0.5 // indirect - golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a - golang.org/x/net v0.0.0-20200822124328-c89045814202 // indirect + github.com/sirupsen/logrus v1.7.0 // indirect + github.com/spf13/cobra v1.1.1 + golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9 + golang.org/x/net v0.0.0-20201110031124-69a78807bb2b // indirect golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a // indirect - golang.org/x/sys v0.0.0-20200821140526-fda516888d29 // indirect + golang.org/x/sys v0.0.0-20201113233024-12cec1faf1ba // indirect golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df diff --git a/go.sum b/go.sum index e3341365..f2f9f209 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,24 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/goquery v1.5.1 h1:PSPBGne8NIUWw+/7vFBV+kG2J/5MOjbzc7154OaKCSE= github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= +github.com/PuerkitoBio/goquery v1.6.0 h1:j7taAbelrdcsOlGeMenZxc2AWXD5fieT1/znArdnx94= +github.com/PuerkitoBio/goquery v1.6.0/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= @@ -33,6 +47,7 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -44,8 +59,10 @@ github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:z github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= @@ -81,6 +98,7 @@ github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVB github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= @@ -107,6 +125,8 @@ github.com/gobuffalo/packr/v2 v2.7.1 h1:n3CIW5T17T8v4GGK5sWXLVWJhCz7b5aNLSxW6gYi github.com/gobuffalo/packr/v2 v2.7.1/go.mod h1:qYEvAazPaVxy7Y7KR0W8qYEE+RymX74kETFqjFoFlOc= github.com/gobuffalo/packr/v2 v2.8.0 h1:IULGd15bQL59ijXLxEvA5wlMxsmx/ZkQv9T282zNVIY= github.com/gobuffalo/packr/v2 v2.8.0/go.mod h1:PDk2k3vGevNE3SwVyVRgQCCXETC9SaONCNSXT1Q8M1g= +github.com/gobuffalo/packr/v2 v2.8.1 h1:tkQpju6i3EtMXJ9uoF5GT6kB+LMTimDWD8Xvbz6zDVA= +github.com/gobuffalo/packr/v2 v2.8.1/go.mod h1:c/PLlOuTU+p3SybaJATW3H6lX/iK7xEz5OeMf+NnJpg= github.com/godror/godror v0.13.3/go.mod h1:2ouUT4kdhUBk7TAkHWD4SN0CdI0pgEQbo8FVHhbSKWg= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -119,6 +139,8 @@ github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4er github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -133,15 +155,24 @@ github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3 h1:x95R7cp+rSeeqAMI2knLtQ0DKlaBhv2NrtrOvafPHRo= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= @@ -154,12 +185,15 @@ github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= @@ -200,12 +234,14 @@ github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22 github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/karrick/godirwalk v1.15.3 h1:0a2pXOgtB16CqIqXTiT7+K9L73f74n/aNQUnH6Ortew= github.com/karrick/godirwalk v1.15.3/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= github.com/karrick/godirwalk v1.15.6 h1:Yf2mmR8TJy+8Fa0SuQVto5SYap6IF7lNVX4Jdl8G1qA= github.com/karrick/godirwalk v1.15.6/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= +github.com/karrick/godirwalk v1.15.8/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw= github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= @@ -232,6 +268,7 @@ github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-b github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/markbates/errx v1.1.0 h1:QDFeR+UP95dO12JgW+tgi2UVfo0V8YBHiUIOaeBPiEI= github.com/markbates/errx v1.1.0/go.mod h1:PLa46Oex9KNbVDZhKel8v1OT7hD5JZ2eI7AHhA0wswc= github.com/markbates/oncer v1.0.0 h1:E83IaVAHygyndzPimgUYJjbshhDTALZyXxvk9FOlQRY= @@ -244,6 +281,8 @@ github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+v github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw= github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= @@ -363,6 +402,8 @@ github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4 github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= @@ -376,6 +417,8 @@ github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tL github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8= github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4= +github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= @@ -384,6 +427,7 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= @@ -395,6 +439,7 @@ github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJy github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= @@ -410,6 +455,8 @@ go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -425,6 +472,7 @@ golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4 h1:ydJNl0ENAG67pFbB+9tfhiL2pYqLhfoaZFw/cjLhY4A= golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -436,13 +484,26 @@ golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9 h1:umElSU9WZirRdgu2yFHY0ayQkEnKiOC1TtM3fWXFnoU= +golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -458,6 +519,8 @@ golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -471,8 +534,11 @@ golang.org/x/net v0.0.0-20200513185701-a91f0712d120 h1:EZ3cVSzKOlJxAd8e8YAJ7no8n golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -492,12 +558,17 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -506,10 +577,16 @@ golang.org/x/sys v0.0.0-20200513112337-417ce2331b5c h1:kISX68E8gSkNYAFRFiDU8rl5R golang.org/x/sys v0.0.0-20200513112337-417ce2331b5c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200821140526-fda516888d29 h1:mNuhGagCf3lDDm5C0376C/sxh6V7fy9WbdEu/YDNA04= golang.org/x/sys v0.0.0-20200821140526-fda516888d29/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201113233024-12cec1faf1ba h1:xmhUJGQGbxlod18iJGqVEp9cHIPLl7QiX2aA3to708s= +golang.org/x/sys v0.0.0-20201113233024-12cec1faf1ba/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -523,13 +600,22 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191004055002-72853e10c5a3/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200308013534-11ec41452d41 h1:9Di9iYgOt9ThCipBxChBVhgNipDoE5mxO84rQV7D0FE= @@ -539,22 +625,35 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= @@ -575,6 +674,7 @@ gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AW gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= gopkg.in/gorp.v1 v1.7.2 h1:j3DWlAyGVv8whO7AcIWznQ2Yj7yJkn34B8s63GViAAw= gopkg.in/gorp.v1 v1.7.2/go.mod h1:Wo3h+DBQZIxATwftsglhdD/62zRFPhGhTiu5jUJmCaw= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= @@ -584,11 +684,15 @@ gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/jslib/package-lock.json b/jslib/package-lock.json index a7aa15e1..045f649e 100644 --- a/jslib/package-lock.json +++ b/jslib/package-lock.json @@ -434,9 +434,9 @@ } }, "@types/history": { - "version": "4.7.7", - "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.7.tgz", - "integrity": "sha512-2xtoL22/3Mv6a70i4+4RB7VgbDDORoWwjcqeNysojZA0R7NK17RbY5Gof/2QiFfJgX+KkWghbwJ+d/2SB8Ndzg==" + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.8.tgz", + "integrity": "sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA==" }, "@types/istanbul-lib-coverage": { "version": "2.0.1", diff --git a/jslib/package.json b/jslib/package.json index 4076ce63..ad602990 100644 --- a/jslib/package.json +++ b/jslib/package.json @@ -16,7 +16,7 @@ "author": "Monomax Software Pty Ltd", "license": "AGPL-3.0-or-later", "dependencies": { - "@types/history": "^4.7.7", + "@types/history": "^4.7.8", "history": "^4.10.1", "lodash": "^4.17.20", "qs": "^6.9.4" diff --git a/package-lock.json b/package-lock.json index c36ca8a0..0d41ecac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -78,18 +78,18 @@ "dev": true }, "@babel/runtime": { - "version": "7.11.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.11.2.tgz", - "integrity": "sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.5.tgz", + "integrity": "sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==", "dev": true, "requires": { "regenerator-runtime": "^0.13.4" } }, "@babel/runtime-corejs3": { - "version": "7.11.2", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.11.2.tgz", - "integrity": "sha512-qh5IR+8VgFz83VBa6OkaET6uN/mJOhHONuy3m1sgF0CV6mXdPSEBdA7e1eUbVvyNtANjMbg22JUv71BaDXLY6A==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.12.5.tgz", + "integrity": "sha512-roGr54CsTmNPPzZoCP1AmDXuBoNao7tnSA83TXTwt+UK5QVyh1DIJnrgYRPWKCF2flqZQXwa7Yr8v7VmLzF0YQ==", "dev": true, "requires": { "core-js-pure": "^3.0.0", @@ -379,9 +379,9 @@ "dev": true }, "axe-core": { - "version": "3.5.5", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-3.5.5.tgz", - "integrity": "sha512-5P0QZ6J5xGikH780pghEdbEKijCTrruK9KxtPZCFWUpef0f6GipO+xEZ5GKCb020mmqgbiNO6TcA55CriL784Q==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.0.2.tgz", + "integrity": "sha512-arU1h31OGFu+LPrOLGZ7nB45v940NMDMEJeNmbutu57P+UFDVnkZg3e+J1I2HJRZ9hT7gO8J91dn/PMrAiKakA==", "dev": true }, "axobject-query": { @@ -420,6 +420,16 @@ "concat-map": "0.0.1" } }, + "call-bind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.0.tgz", + "integrity": "sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.0" + } + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -480,9 +490,9 @@ "dev": true }, "confusing-browser-globals": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.9.tgz", - "integrity": "sha512-KbS1Y0jMtyPgIxjO7ZzMAuUpAKMt1SzCL9fsrKsX6b0zJPTaT0SiSPmewwVZg9UAO83HVIlEhZF84LIjZ0lmAw==", + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.10.tgz", + "integrity": "sha512-gNld/3lySHwuhaVluJUKLePYirM3QNCKzVxqAdhJII9/WXKVX5PURzMVJspS1jTslSqjeuG4KMVTSouit5YPHA==", "dev": true }, "contains-path": { @@ -492,9 +502,9 @@ "dev": true }, "core-js-pure": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.6.5.tgz", - "integrity": "sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA==", + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.7.0.tgz", + "integrity": "sha512-EZD2ckZysv8MMt4J6HSvS9K2GdtlZtdBncKAmF9lr2n0c9dJUaUN88PSTjvgwCgQPWKTkERXITgS6JJRAnljtg==", "dev": true }, "cross-spawn": { @@ -573,20 +583,20 @@ } }, "es-abstract": { - "version": "1.17.6", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", - "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", + "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", "dev": true, "requires": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "has": "^1.0.3", "has-symbols": "^1.0.1", - "is-callable": "^1.2.0", - "is-regex": "^1.1.0", - "object-inspect": "^1.7.0", + "is-callable": "^1.2.2", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", "object-keys": "^1.1.1", - "object.assign": "^4.1.0", + "object.assign": "^4.1.1", "string.prototype.trimend": "^1.0.1", "string.prototype.trimstart": "^1.0.1" } @@ -671,31 +681,31 @@ } }, "eslint-config-airbnb": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-18.2.0.tgz", - "integrity": "sha512-Fz4JIUKkrhO0du2cg5opdyPKQXOI2MvF8KUvN2710nJMT6jaRUpRE2swrJftAjVGL7T1otLM5ieo5RqS1v9Udg==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-18.2.1.tgz", + "integrity": "sha512-glZNDEZ36VdlZWoxn/bUR1r/sdFKPd1mHPbqUtkctgNG4yT2DLLtJ3D+yCV+jzZCc2V1nBVkmdknOJBZ5Hc0fg==", "dev": true, "requires": { - "eslint-config-airbnb-base": "^14.2.0", - "object.assign": "^4.1.0", + "eslint-config-airbnb-base": "^14.2.1", + "object.assign": "^4.1.2", "object.entries": "^1.1.2" } }, "eslint-config-airbnb-base": { - "version": "14.2.0", - "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.0.tgz", - "integrity": "sha512-Snswd5oC6nJaevs3nZoLSTvGJBvzTfnBqOIArkf3cbyTyq9UD79wOk8s+RiL6bhca0p/eRO6veczhf6A/7Jy8Q==", + "version": "14.2.1", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.1.tgz", + "integrity": "sha512-GOrQyDtVEc1Xy20U7vsB2yAoB4nBlfH5HZJeatRXHleO+OS5Ot+MWij4Dpltw4/DyIkqUfqz1epfhVR5XWWQPA==", "dev": true, "requires": { - "confusing-browser-globals": "^1.0.9", - "object.assign": "^4.1.0", + "confusing-browser-globals": "^1.0.10", + "object.assign": "^4.1.2", "object.entries": "^1.1.2" } }, "eslint-config-prettier": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.11.0.tgz", - "integrity": "sha512-oB8cpLWSAjOVFEJhhyMZh6NOEOtBVziaqdDQ86+qhDHFbZXoRTM7pNSvFRfW/W/L/LrQ38C99J5CGuRBBzBsdA==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.15.0.tgz", + "integrity": "sha512-a1+kOYLR8wMGustcgAjdydMsQ2A/2ipRPwRKUmfYaSxc9ZPcrku080Ctl6zrZzZNs/U82MjSv+qKREkoq3bJaw==", "dev": true, "requires": { "get-stdin": "^6.0.0" @@ -727,11 +737,12 @@ "dev": true }, "resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", "dev": true, "requires": { + "is-core-module": "^2.1.0", "path-parse": "^1.0.6" } } @@ -765,9 +776,9 @@ } }, "eslint-plugin-import": { - "version": "2.22.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.0.tgz", - "integrity": "sha512-66Fpf1Ln6aIS5Gr/55ts19eUuoDhAbZgnr6UxK5hbDx6l/QgQgx61AePq+BV4PP2uXQFClgMVzep5zZ94qqsxg==", + "version": "2.22.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz", + "integrity": "sha512-8K7JjINHOpH64ozkAhpT3sd+FswIZTfMZTjdx052pnWrgRCVfp8op9tbjpAk3DdUeI/Ba4C8OjdC0r90erHEOw==", "dev": true, "requires": { "array-includes": "^3.1.1", @@ -775,7 +786,7 @@ "contains-path": "^0.1.0", "debug": "^2.6.9", "doctrine": "1.5.0", - "eslint-import-resolver-node": "^0.3.3", + "eslint-import-resolver-node": "^0.3.4", "eslint-module-utils": "^2.6.0", "has": "^1.0.3", "minimatch": "^3.0.4", @@ -811,39 +822,40 @@ "dev": true }, "resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", "dev": true, "requires": { + "is-core-module": "^2.1.0", "path-parse": "^1.0.6" } } } }, "eslint-plugin-jsx-a11y": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.3.1.tgz", - "integrity": "sha512-i1S+P+c3HOlBJzMFORRbC58tHa65Kbo8b52/TwCwSKLohwvpfT5rm2GjGWzOHTEuq4xxf2aRlHHTtmExDQOP+g==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.4.1.tgz", + "integrity": "sha512-0rGPJBbwHoGNPU73/QCLP/vveMlM1b1Z9PponxO87jfr6tuH5ligXbDT6nHSSzBC8ovX2Z+BQu7Bk5D/Xgq9zg==", "dev": true, "requires": { - "@babel/runtime": "^7.10.2", + "@babel/runtime": "^7.11.2", "aria-query": "^4.2.2", "array-includes": "^3.1.1", "ast-types-flow": "^0.0.7", - "axe-core": "^3.5.4", - "axobject-query": "^2.1.2", + "axe-core": "^4.0.2", + "axobject-query": "^2.2.0", "damerau-levenshtein": "^1.0.6", "emoji-regex": "^9.0.0", "has": "^1.0.3", - "jsx-ast-utils": "^2.4.1", + "jsx-ast-utils": "^3.1.0", "language-tags": "^1.0.5" }, "dependencies": { "emoji-regex": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.0.0.tgz", - "integrity": "sha512-6p1NII1Vm62wni/VR/cUMauVQoxmLVb9csqQlvLz+hO2gk8U2UYDfXHQSUYIBKmZwAKz867IDqG7B+u0mj+M6w==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.0.tgz", + "integrity": "sha512-DNc3KFPK18bPdElMJnf/Pkv5TXhxFU3YFDEuGLDRtPmV4rkmCjBkCSEp22u6rBHdSN9Vlp/GK7k98prmE1Jgug==", "dev": true } } @@ -858,21 +870,21 @@ } }, "eslint-plugin-react": { - "version": "7.20.6", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.20.6.tgz", - "integrity": "sha512-kidMTE5HAEBSLu23CUDvj8dc3LdBU0ri1scwHBZjI41oDv4tjsWZKU7MQccFzH1QYPYhsnTF2ovh7JlcIcmxgg==", + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.21.5.tgz", + "integrity": "sha512-8MaEggC2et0wSF6bUeywF7qQ46ER81irOdWS4QWxnnlAEsnzeBevk1sWh7fhpCghPpXb+8Ks7hvaft6L/xsR6g==", "dev": true, "requires": { "array-includes": "^3.1.1", "array.prototype.flatmap": "^1.2.3", "doctrine": "^2.1.0", "has": "^1.0.3", - "jsx-ast-utils": "^2.4.1", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", "object.entries": "^1.1.2", "object.fromentries": "^2.0.2", "object.values": "^1.1.1", "prop-types": "^15.7.2", - "resolve": "^1.17.0", + "resolve": "^1.18.1", "string.prototype.matchall": "^4.0.2" }, "dependencies": { @@ -886,11 +898,12 @@ } }, "resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", "dev": true, "requires": { + "is-core-module": "^2.1.0", "path-parse": "^1.0.6" } } @@ -1071,6 +1084,17 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, + "get-intrinsic": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.1.tgz", + "integrity": "sha512-ZnWP+AmS1VUaLgTRy47+zKtjTxz+0xMpx3I52i+aalBK1QP19ggLF3Db89KJX7kjfOfP2eoa01qc++GwPgufPg==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, "get-stdin": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", @@ -1225,11 +1249,20 @@ "dev": true }, "is-callable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", - "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", "dev": true }, + "is-core-module": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.1.0.tgz", + "integrity": "sha512-YcV7BgVMRFRua2FqQzKtTDMz8iCuLEyGKjr70q8Zm1yy2qKcurbFEd79PAdHV77oL3NrAaOVQIbMmiHQCHB7ZA==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, "is-date-object": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", @@ -1349,19 +1382,19 @@ } }, "jsx-ast-utils": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.4.1.tgz", - "integrity": "sha512-z1xSldJ6imESSzOjd3NNkieVJKRlKYSOtMG8SFyCj2FIrvSaSuli/WjpBkEzCBoR9bYYYFgqJw61Xhu7Lcgk+w==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.1.0.tgz", + "integrity": "sha512-d4/UOjg+mxAWxCiF0c5UTSwyqbchkbqCvK87aBovhnh8GtysTjWmgC63tY0cJx/HzGgm9qnA147jVBdpOiQ2RA==", "dev": true, "requires": { "array-includes": "^3.1.1", - "object.assign": "^4.1.0" + "object.assign": "^4.1.1" } }, "language-subtag-registry": { - "version": "0.3.20", - "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.20.tgz", - "integrity": "sha512-KPMwROklF4tEx283Xw0pNKtfTj1gZ4UByp4EsIFWLgBavJltF4TiYPc39k06zSTsLzxTVXXDSpbwaQXaFB4Qeg==", + "version": "0.3.21", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz", + "integrity": "sha512-L0IqwlIXjilBVVYKFT37X9Ih11Um5NEl9cbJIuU/SwP/zEEAbBPOnEeeuxVMf45ydWQRDQN3Nqc96OgbH1K+Pg==", "dev": true }, "language-tags": { @@ -1513,15 +1546,15 @@ "dev": true }, "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", "dev": true, "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" } }, "object.entries": { @@ -1694,9 +1727,9 @@ "dev": true }, "prettier": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.5.tgz", - "integrity": "sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.1.2.tgz", + "integrity": "sha512-16c7K+x4qVlJg9rEbXl7HEGmQyZlG4R9AgP+oHKRMsMsuk8s+ATStlf1NpDqyBI1HpVyfjLOeMhH2LvuNvV5Vg==", "dev": true }, "prettier-linter-helpers": { @@ -1870,21 +1903,21 @@ }, "dependencies": { "es-abstract": { - "version": "1.18.0-next.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.0.tgz", - "integrity": "sha512-elZXTZXKn51hUBdJjSZGYRujuzilgXo8vSPQzjGYXLvSlGiCo8VO8ZGV3kjo9a0WNJJ57hENagwbtlRuHuzkcQ==", + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", "dev": true, "requires": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "has": "^1.0.3", "has-symbols": "^1.0.1", - "is-callable": "^1.2.0", + "is-callable": "^1.2.2", "is-negative-zero": "^2.0.0", "is-regex": "^1.1.1", "object-inspect": "^1.8.0", "object-keys": "^1.1.1", - "object.assign": "^4.1.0", + "object.assign": "^4.1.1", "string.prototype.trimend": "^1.0.1", "string.prototype.trimstart": "^1.0.1" } @@ -1949,9 +1982,9 @@ } }, "spdx-license-ids": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", - "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz", + "integrity": "sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw==", "dev": true }, "sprintf-js": { @@ -1997,23 +2030,67 @@ } }, "string.prototype.trimend": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", - "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.2.tgz", + "integrity": "sha512-8oAG/hi14Z4nOVP0z6mdiVZ/wqjDtWSLygMigTzAb+7aPEDTleeFf+WrF+alzecxIRkckkJVn+dTlwzJXORATw==", "dev": true, "requires": { "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" + "es-abstract": "^1.18.0-next.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + } } }, "string.prototype.trimstart": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", - "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.2.tgz", + "integrity": "sha512-7F6CdBTl5zyu30BJFdzSTlSlLPwODC23Od+iLoVH8X6+3fvDPPuBVVj9iaB1GOsSTSIgVfsfm27R2FGrAPznWg==", "dev": true, "requires": { "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" + "es-abstract": "^1.18.0-next.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + } } }, "strip-ansi": { diff --git a/package.json b/package.json index 13179fed..71d0849f 100644 --- a/package.json +++ b/package.json @@ -9,14 +9,14 @@ "@typescript-eslint/parser": "^2.34.0", "babel-eslint": "^10.1.0", "eslint": "^6.8.0", - "eslint-config-airbnb": "^18.2.0", - "eslint-config-prettier": "^6.11.0", - "eslint-plugin-import": "^2.22.0", - "eslint-plugin-jsx-a11y": "^6.3.1", + "eslint-config-airbnb": "^18.2.1", + "eslint-config-prettier": "^6.15.0", + "eslint-plugin-import": "^2.22.1", + "eslint-plugin-jsx-a11y": "^6.4.1", "eslint-plugin-prettier": "^3.1.4", - "eslint-plugin-react": "^7.20.6", + "eslint-plugin-react": "^7.21.5", "eslint-plugin-react-hooks": "^2.5.1", - "prettier": "^2.0.0", + "prettier": "^2.1.2", "typescript": "^3.9.7" } } diff --git a/web/package-lock.json b/web/package-lock.json index 7237635f..386fa985 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -14,30 +14,25 @@ } }, "@babel/compat-data": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.11.0.tgz", - "integrity": "sha512-TPSvJfv73ng0pfnEOh17bYMPQbI95+nGWc71Ss4vZdRBHTDqmM9Z8ZV4rYz8Ks7sfzc95n30k6ODIq5UGnXcYQ==", - "dev": true, - "requires": { - "browserslist": "^4.12.0", - "invariant": "^2.2.4", - "semver": "^5.5.0" - } + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.12.5.tgz", + "integrity": "sha512-DTsS7cxrsH3by8nqQSpFSyjSfSYl57D6Cf4q8dW3LK83tBKBDCkfcay1nYkXq1nIHXnpX8WMMb/O25HOy3h1zg==", + "dev": true }, "@babel/core": { - "version": "7.11.4", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.11.4.tgz", - "integrity": "sha512-5deljj5HlqRXN+5oJTY7Zs37iH3z3b++KjiKtIsJy1NrjOOVSEaJHEetLBhyu0aQOSNNZ/0IuEAan9GzRuDXHg==", + "version": "7.12.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.3.tgz", + "integrity": "sha512-0qXcZYKZp3/6N2jKYVxZv0aNCsxTSVCiK72DTiTYZAu7sjg73W0/aynWjMbiGd87EQL4WyA8reiJVh92AVla9g==", "dev": true, "requires": { "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.11.4", - "@babel/helper-module-transforms": "^7.11.0", - "@babel/helpers": "^7.10.4", - "@babel/parser": "^7.11.4", + "@babel/generator": "^7.12.1", + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helpers": "^7.12.1", + "@babel/parser": "^7.12.3", "@babel/template": "^7.10.4", - "@babel/traverse": "^7.11.0", - "@babel/types": "^7.11.0", + "@babel/traverse": "^7.12.1", + "@babel/types": "^7.12.1", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.1", @@ -58,12 +53,12 @@ } }, "@babel/generator": { - "version": "7.11.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.4.tgz", - "integrity": "sha512-Rn26vueFx0eOoz7iifCN2UHT6rGtnkSGWSoDRIy8jZN3B91PzeSULbswfLoOWuTuAcNwpG/mxy+uCTDnZ9Mp1g==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.5.tgz", + "integrity": "sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A==", "dev": true, "requires": { - "@babel/types": "^7.11.0", + "@babel/types": "^7.12.5", "jsesc": "^2.5.1", "source-map": "^0.5.0" } @@ -115,9 +110,9 @@ } }, "@babel/parser": { - "version": "7.11.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.4.tgz", - "integrity": "sha512-MggwidiH+E9j5Sh8pbrX5sJvMcsqS5o+7iB42M9/k0CD63MjYbdP4nhSh7uB5wnv2/RVzTZFTxzF/kIa5mrCqA==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.5.tgz", + "integrity": "sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ==", "dev": true }, "@babel/template": { @@ -132,26 +127,26 @@ } }, "@babel/traverse": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.0.tgz", - "integrity": "sha512-ZB2V+LskoWKNpMq6E5UUCrjtDUh5IOTAyIl0dTjIEoXum/iKWkoIEKIRDnUucO6f+2FzNkE0oD4RLKoPIufDtg==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.5.tgz", + "integrity": "sha512-xa15FbQnias7z9a62LwYAA5SZZPkHIXpd42C6uW68o8uTuua96FHZy1y61Va5P/i83FAAcMpW8+A/QayntzuqA==", "dev": true, "requires": { "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.11.0", + "@babel/generator": "^7.12.5", "@babel/helper-function-name": "^7.10.4", "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/parser": "^7.11.0", - "@babel/types": "^7.11.0", + "@babel/parser": "^7.12.5", + "@babel/types": "^7.12.5", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.19" } }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", @@ -167,12 +162,6 @@ "requires": { "minimist": "^1.2.5" } - }, - "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", - "dev": true } } }, @@ -204,21 +193,15 @@ "dev": true }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", "lodash": "^4.17.19", "to-fast-properties": "^2.0.0" } - }, - "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", - "dev": true } } }, @@ -239,21 +222,15 @@ "dev": true }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", "lodash": "^4.17.19", "to-fast-properties": "^2.0.0" } - }, - "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", - "dev": true } } }, @@ -274,33 +251,27 @@ "dev": true }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", "lodash": "^4.17.19", "to-fast-properties": "^2.0.0" } - }, - "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", - "dev": true } } }, "@babel/helper-builder-react-jsx-experimental": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-react-jsx-experimental/-/helper-builder-react-jsx-experimental-7.10.5.tgz", - "integrity": "sha512-Buewnx6M4ttG+NLkKyt7baQn7ScC/Td+e99G914fRU8fGIUivDDgVIQeDHFa5e4CRSJQt58WpNHhsAZgtzVhsg==", + "version": "7.12.4", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-react-jsx-experimental/-/helper-builder-react-jsx-experimental-7.12.4.tgz", + "integrity": "sha512-AjEa0jrQqNk7eDQOo0pTfUOwQBMF+xVqrausQwT9/rTKy0g04ggFNaJpaE09IQMn9yExluigWMJcj0WC7bq+Og==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-module-imports": "^7.10.4", - "@babel/types": "^7.10.5" + "@babel/helper-module-imports": "^7.12.1", + "@babel/types": "^7.12.1" }, "dependencies": { "@babel/helper-validator-identifier": { @@ -310,47 +281,72 @@ "dev": true }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", "lodash": "^4.17.19", "to-fast-properties": "^2.0.0" } - }, - "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", - "dev": true } } }, "@babel/helper-compilation-targets": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.10.4.tgz", - "integrity": "sha512-a3rYhlsGV0UHNDvrtOXBg8/OpfV0OKTkxKPzIplS1zpx7CygDcWWxckxZeDd3gzPzC4kUT0A4nVFDK0wGMh4MQ==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.12.5.tgz", + "integrity": "sha512-+qH6NrscMolUlzOYngSBMIOQpKUGPPsc61Bu5W10mg84LxZ7cmvnBHzARKbDoFxVvqqAbj6Tg6N7bSrWSPXMyw==", "dev": true, "requires": { - "@babel/compat-data": "^7.10.4", - "browserslist": "^4.12.0", - "invariant": "^2.2.4", - "levenary": "^1.1.1", + "@babel/compat-data": "^7.12.5", + "@babel/helper-validator-option": "^7.12.1", + "browserslist": "^4.14.5", "semver": "^5.5.0" + }, + "dependencies": { + "browserslist": { + "version": "4.14.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.7.tgz", + "integrity": "sha512-BSVRLCeG3Xt/j/1cCGj1019Wbty0H+Yvu2AOuZSuoaUWn3RatbL33Cxk+Q4jRMRAbOm0p7SLravLjpnT6s0vzQ==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001157", + "colorette": "^1.2.1", + "electron-to-chromium": "^1.3.591", + "escalade": "^3.1.1", + "node-releases": "^1.1.66" + } + }, + "caniuse-lite": { + "version": "1.0.30001158", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001158.tgz", + "integrity": "sha512-s5loVYY+yKpuVA3HyW8BarzrtJvwHReuzugQXlv1iR3LKSReoFXRm86mT6hT7PEF5RxW+XQZg+6nYjlywYzQ+g==", + "dev": true + }, + "electron-to-chromium": { + "version": "1.3.596", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.596.tgz", + "integrity": "sha512-nLO2Wd2yU42eSoNJVQKNf89CcEGqeFZd++QsnN2XIgje1s/19AgctfjLIbPORlvcCO8sYjLwX4iUgDdusOY8Sg==", + "dev": true + }, + "node-releases": { + "version": "1.1.66", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.66.tgz", + "integrity": "sha512-JHEQ1iWPGK+38VLB2H9ef2otU4l8s3yAMt9Xf934r6+ojCYDMHPMqvCc9TnzfeFSP1QEOeU6YZEd3+De0LTCgg==", + "dev": true + } } }, "@babel/helper-create-class-features-plugin": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.5.tgz", - "integrity": "sha512-0nkdeijB7VlZoLT3r/mY3bUkw3T8WG/hNw+FATs/6+pG2039IJWjTYL0VTISqsNHMUTEnwbVnc89WIJX9Qed0A==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.12.1.tgz", + "integrity": "sha512-hkL++rWeta/OVOBTRJc9a5Azh5mt5WgZUGAKMD8JM141YsE08K//bp1unBBieO6rUKkIPyUE0USQ30jAy3Sk1w==", "requires": { "@babel/helper-function-name": "^7.10.4", - "@babel/helper-member-expression-to-functions": "^7.10.5", + "@babel/helper-member-expression-to-functions": "^7.12.1", "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-replace-supers": "^7.10.4", + "@babel/helper-replace-supers": "^7.12.1", "@babel/helper-split-export-declaration": "^7.10.4" }, "dependencies": { @@ -380,11 +376,6 @@ "@babel/types": "^7.10.4" } }, - "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==" - }, "@babel/helper-split-export-declaration": { "version": "7.11.0", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", @@ -409,9 +400,9 @@ } }, "@babel/parser": { - "version": "7.11.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.4.tgz", - "integrity": "sha512-MggwidiH+E9j5Sh8pbrX5sJvMcsqS5o+7iB42M9/k0CD63MjYbdP4nhSh7uB5wnv2/RVzTZFTxzF/kIa5mrCqA==" + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.5.tgz", + "integrity": "sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ==" }, "@babel/template": { "version": "7.10.4", @@ -424,31 +415,26 @@ } }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "requires": { "@babel/helper-validator-identifier": "^7.10.4", "lodash": "^4.17.19", "to-fast-properties": "^2.0.0" } - }, - "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" } } }, "@babel/helper-create-regexp-features-plugin": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.4.tgz", - "integrity": "sha512-2/hu58IEPKeoLF45DBwx3XFqsbCXmkdAay4spVr2x0jYgRxrSNp+ePwvSsy9g6YSaNDcKIQVPXk1Ov8S2edk2g==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.12.1.tgz", + "integrity": "sha512-rsZ4LGvFTZnzdNZR5HZdmJVuXK8834R5QkF3WvcnBhrlVtF0HSIUC6zbreL9MgjTywhKokn8RIYRiq99+DLAxA==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.10.4", "@babel/helper-regex": "^7.10.4", - "regexpu-core": "^4.7.0" + "regexpu-core": "^4.7.1" } }, "@babel/helper-define-map": { @@ -509,9 +495,9 @@ } }, "@babel/parser": { - "version": "7.11.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.4.tgz", - "integrity": "sha512-MggwidiH+E9j5Sh8pbrX5sJvMcsqS5o+7iB42M9/k0CD63MjYbdP4nhSh7uB5wnv2/RVzTZFTxzF/kIa5mrCqA==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.5.tgz", + "integrity": "sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ==", "dev": true }, "@babel/template": { @@ -526,31 +512,25 @@ } }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", "lodash": "^4.17.19", "to-fast-properties": "^2.0.0" } - }, - "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", - "dev": true } } }, "@babel/helper-explode-assignable-expression": { - "version": "7.11.4", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.11.4.tgz", - "integrity": "sha512-ux9hm3zR4WV1Y3xXxXkdG/0gxF9nvI0YVmKVhvK9AfMoaQkemL3sJpXw+Xbz65azo8qJiEz2XVDUpK3KYhH3ZQ==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.12.1.tgz", + "integrity": "sha512-dmUwH8XmlrUpVqgtZ737tK88v07l840z9j3OEhCLwKTkjlvKpfqXVIZ0wpK3aeOxspwGrf/5AP5qLx4rO3w5rA==", "dev": true, "requires": { - "@babel/types": "^7.10.4" + "@babel/types": "^7.12.1" }, "dependencies": { "@babel/helper-validator-identifier": { @@ -560,21 +540,15 @@ "dev": true }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", "lodash": "^4.17.19", "to-fast-properties": "^2.0.0" } - }, - "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", - "dev": true } } }, @@ -614,30 +588,24 @@ "dev": true }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", "lodash": "^4.17.19", "to-fast-properties": "^2.0.0" } - }, - "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", - "dev": true } } }, "@babel/helper-member-expression-to-functions": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.11.0.tgz", - "integrity": "sha512-JbFlKHFntRV5qKw3YC0CvQnDZ4XMwgzzBbld7Ly4Mj4cbFy3KywcR8NtNctRToMWJOVvLINJv525Gd6wwVEx/Q==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.1.tgz", + "integrity": "sha512-k0CIe3tXUKTRSoEx1LQEPFU9vRQfqHtl+kf8eNnDqb4AUJEy5pz6aIiog+YWtVm2jpggjS1laH68bPsR+KWWPQ==", "requires": { - "@babel/types": "^7.11.0" + "@babel/types": "^7.12.1" }, "dependencies": { "@babel/helper-validator-identifier": { @@ -646,29 +614,24 @@ "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==" }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "requires": { "@babel/helper-validator-identifier": "^7.10.4", "lodash": "^4.17.19", "to-fast-properties": "^2.0.0" } - }, - "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" } } }, "@babel/helper-module-imports": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz", - "integrity": "sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", + "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", "dev": true, "requires": { - "@babel/types": "^7.10.4" + "@babel/types": "^7.12.5" }, "dependencies": { "@babel/helper-validator-identifier": { @@ -678,36 +641,32 @@ "dev": true }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", "lodash": "^4.17.19", "to-fast-properties": "^2.0.0" } - }, - "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", - "dev": true } } }, "@babel/helper-module-transforms": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz", - "integrity": "sha512-02EVu8COMuTRO1TAzdMtpBPbe6aQ1w/8fePD2YgQmxZU4gpNWaL9gK3Jp7dxlkUlUCJOTaSeA+Hrm1BRQwqIhg==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz", + "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.10.4", - "@babel/helper-replace-supers": "^7.10.4", - "@babel/helper-simple-access": "^7.10.4", + "@babel/helper-module-imports": "^7.12.1", + "@babel/helper-replace-supers": "^7.12.1", + "@babel/helper-simple-access": "^7.12.1", "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/helper-validator-identifier": "^7.10.4", "@babel/template": "^7.10.4", - "@babel/types": "^7.11.0", + "@babel/traverse": "^7.12.1", + "@babel/types": "^7.12.1", "lodash": "^4.17.19" }, "dependencies": { @@ -720,6 +679,37 @@ "@babel/highlight": "^7.10.4" } }, + "@babel/generator": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.5.tgz", + "integrity": "sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A==", + "dev": true, + "requires": { + "@babel/types": "^7.12.5", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-function-name": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", + "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", + "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, "@babel/helper-split-export-declaration": { "version": "7.11.0", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", @@ -747,9 +737,9 @@ } }, "@babel/parser": { - "version": "7.11.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.4.tgz", - "integrity": "sha512-MggwidiH+E9j5Sh8pbrX5sJvMcsqS5o+7iB42M9/k0CD63MjYbdP4nhSh7uB5wnv2/RVzTZFTxzF/kIa5mrCqA==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.5.tgz", + "integrity": "sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ==", "dev": true }, "@babel/template": { @@ -763,22 +753,33 @@ "@babel/types": "^7.10.4" } }, + "@babel/traverse": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.5.tgz", + "integrity": "sha512-xa15FbQnias7z9a62LwYAA5SZZPkHIXpd42C6uW68o8uTuua96FHZy1y61Va5P/i83FAAcMpW8+A/QayntzuqA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.5", + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/parser": "^7.12.5", + "@babel/types": "^7.12.5", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.19" + } + }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", "lodash": "^4.17.19", "to-fast-properties": "^2.0.0" } - }, - "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", - "dev": true } } }, @@ -796,19 +797,14 @@ "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==" }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "requires": { "@babel/helper-validator-identifier": "^7.10.4", "lodash": "^4.17.19", "to-fast-properties": "^2.0.0" } - }, - "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" } } }, @@ -825,99 +821,47 @@ "dev": true, "requires": { "lodash": "^4.17.19" - }, - "dependencies": { - "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", - "dev": true - } } }, "@babel/helper-remap-async-to-generator": { - "version": "7.11.4", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.11.4.tgz", - "integrity": "sha512-tR5vJ/vBa9wFy3m5LLv2faapJLnDFxNWff2SAYkSE4rLUdbp7CdObYFgI7wK4T/Mj4UzpjPwzR8Pzmr5m7MHGA==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.12.1.tgz", + "integrity": "sha512-9d0KQCRM8clMPcDwo8SevNs+/9a8yWVVmaE80FGJcEP8N1qToREmWEGnBn8BUlJhYRFz6fqxeRL1sl5Ogsed7A==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.10.4", "@babel/helper-wrap-function": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.4" + "@babel/types": "^7.12.1" }, "dependencies": { - "@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, "@babel/helper-validator-identifier": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", "dev": true }, - "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "@babel/parser": { - "version": "7.11.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.4.tgz", - "integrity": "sha512-MggwidiH+E9j5Sh8pbrX5sJvMcsqS5o+7iB42M9/k0CD63MjYbdP4nhSh7uB5wnv2/RVzTZFTxzF/kIa5mrCqA==", - "dev": true - }, - "@babel/template": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", - "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", "lodash": "^4.17.19", "to-fast-properties": "^2.0.0" } - }, - "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", - "dev": true } } }, "@babel/helper-replace-supers": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz", - "integrity": "sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.5.tgz", + "integrity": "sha512-5YILoed0ZyIpF4gKcpZitEnXEJ9UoDRki1Ey6xz46rxOzfNMAhVIJMoune1hmPVxh40LRv1+oafz7UsWX+vyWA==", "requires": { - "@babel/helper-member-expression-to-functions": "^7.10.4", + "@babel/helper-member-expression-to-functions": "^7.12.1", "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" + "@babel/traverse": "^7.12.5", + "@babel/types": "^7.12.5" }, "dependencies": { "@babel/code-frame": { @@ -929,11 +873,11 @@ } }, "@babel/generator": { - "version": "7.11.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.4.tgz", - "integrity": "sha512-Rn26vueFx0eOoz7iifCN2UHT6rGtnkSGWSoDRIy8jZN3B91PzeSULbswfLoOWuTuAcNwpG/mxy+uCTDnZ9Mp1g==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.5.tgz", + "integrity": "sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A==", "requires": { - "@babel/types": "^7.11.0", + "@babel/types": "^7.12.5", "jsesc": "^2.5.1", "source-map": "^0.5.0" } @@ -980,9 +924,9 @@ } }, "@babel/parser": { - "version": "7.11.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.4.tgz", - "integrity": "sha512-MggwidiH+E9j5Sh8pbrX5sJvMcsqS5o+7iB42M9/k0CD63MjYbdP4nhSh7uB5wnv2/RVzTZFTxzF/kIa5mrCqA==" + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.5.tgz", + "integrity": "sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ==" }, "@babel/template": { "version": "7.10.4", @@ -995,117 +939,68 @@ } }, "@babel/traverse": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.0.tgz", - "integrity": "sha512-ZB2V+LskoWKNpMq6E5UUCrjtDUh5IOTAyIl0dTjIEoXum/iKWkoIEKIRDnUucO6f+2FzNkE0oD4RLKoPIufDtg==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.5.tgz", + "integrity": "sha512-xa15FbQnias7z9a62LwYAA5SZZPkHIXpd42C6uW68o8uTuua96FHZy1y61Va5P/i83FAAcMpW8+A/QayntzuqA==", "requires": { "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.11.0", + "@babel/generator": "^7.12.5", "@babel/helper-function-name": "^7.10.4", "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/parser": "^7.11.0", - "@babel/types": "^7.11.0", + "@babel/parser": "^7.12.5", + "@babel/types": "^7.12.5", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.19" } }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "requires": { "@babel/helper-validator-identifier": "^7.10.4", "lodash": "^4.17.19", "to-fast-properties": "^2.0.0" } - }, - "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" } } }, "@babel/helper-simple-access": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz", - "integrity": "sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz", + "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==", "dev": true, "requires": { - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.4" + "@babel/types": "^7.12.1" }, "dependencies": { - "@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, "@babel/helper-validator-identifier": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", "dev": true }, - "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "@babel/parser": { - "version": "7.11.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.4.tgz", - "integrity": "sha512-MggwidiH+E9j5Sh8pbrX5sJvMcsqS5o+7iB42M9/k0CD63MjYbdP4nhSh7uB5wnv2/RVzTZFTxzF/kIa5mrCqA==", - "dev": true - }, - "@babel/template": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", - "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", "lodash": "^4.17.19", "to-fast-properties": "^2.0.0" } - }, - "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", - "dev": true } } }, "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.11.0.tgz", - "integrity": "sha512-0XIdiQln4Elglgjbwo9wuJpL/K7AGCY26kmEt0+pRP0TAj4jjyNq1MjoRvikrTVqKcx4Gysxt4cXvVFXP/JO2Q==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz", + "integrity": "sha512-Mf5AUuhG1/OCChOJ/HcADmvcHM42WJockombn8ATJG3OnyiSxBK/Mm5x78BQWvmtXZKHgbjdGL2kin/HOLlZGA==", "dev": true, "requires": { - "@babel/types": "^7.11.0" + "@babel/types": "^7.12.1" }, "dependencies": { "@babel/helper-validator-identifier": { @@ -1115,21 +1010,15 @@ "dev": true }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", "lodash": "^4.17.19", "to-fast-properties": "^2.0.0" } - }, - "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", - "dev": true } } }, @@ -1148,10 +1037,16 @@ "integrity": "sha512-5vW/JXLALhczRCWP0PnFDMCJAchlBvM7f4uk/jXritBnIa6E1KmqmtrS3yn1LAnxFBypQ3eneLuXjsnfQsgILw==", "dev": true }, + "@babel/helper-validator-option": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.1.tgz", + "integrity": "sha512-YpJabsXlJVWP0USHjnC/AQDTLlZERbON577YUVO/wLpqyj6HAtVYnWaQaN0iUN+1/tWn3c+uKKXjRut5115Y2A==", + "dev": true + }, "@babel/helper-wrap-function": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.10.4.tgz", - "integrity": "sha512-6py45WvEF0MhiLrdxtRjKjufwLL1/ob2qDJgg5JgNdojBAZSAKnAjkyOCNug6n+OBl4VW76XjvgSFTdaMcW0Ug==", + "version": "7.12.3", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.12.3.tgz", + "integrity": "sha512-Cvb8IuJDln3rs6tzjW3Y8UeelAOdnpB8xtQ4sme2MSZ9wOxrbThporC0y/EtE16VAtoyEfLM404Xr1e0OOp+ow==", "dev": true, "requires": { "@babel/helper-function-name": "^7.10.4", @@ -1170,12 +1065,12 @@ } }, "@babel/generator": { - "version": "7.11.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.4.tgz", - "integrity": "sha512-Rn26vueFx0eOoz7iifCN2UHT6rGtnkSGWSoDRIy8jZN3B91PzeSULbswfLoOWuTuAcNwpG/mxy+uCTDnZ9Mp1g==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.5.tgz", + "integrity": "sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A==", "dev": true, "requires": { - "@babel/types": "^7.11.0", + "@babel/types": "^7.12.5", "jsesc": "^2.5.1", "source-map": "^0.5.0" } @@ -1227,9 +1122,9 @@ } }, "@babel/parser": { - "version": "7.11.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.4.tgz", - "integrity": "sha512-MggwidiH+E9j5Sh8pbrX5sJvMcsqS5o+7iB42M9/k0CD63MjYbdP4nhSh7uB5wnv2/RVzTZFTxzF/kIa5mrCqA==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.5.tgz", + "integrity": "sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ==", "dev": true }, "@babel/template": { @@ -1244,50 +1139,44 @@ } }, "@babel/traverse": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.0.tgz", - "integrity": "sha512-ZB2V+LskoWKNpMq6E5UUCrjtDUh5IOTAyIl0dTjIEoXum/iKWkoIEKIRDnUucO6f+2FzNkE0oD4RLKoPIufDtg==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.5.tgz", + "integrity": "sha512-xa15FbQnias7z9a62LwYAA5SZZPkHIXpd42C6uW68o8uTuua96FHZy1y61Va5P/i83FAAcMpW8+A/QayntzuqA==", "dev": true, "requires": { "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.11.0", + "@babel/generator": "^7.12.5", "@babel/helper-function-name": "^7.10.4", "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/parser": "^7.11.0", - "@babel/types": "^7.11.0", + "@babel/parser": "^7.12.5", + "@babel/types": "^7.12.5", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.19" } }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", "lodash": "^4.17.19", "to-fast-properties": "^2.0.0" } - }, - "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", - "dev": true } } }, "@babel/helpers": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.10.4.tgz", - "integrity": "sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.5.tgz", + "integrity": "sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA==", "dev": true, "requires": { "@babel/template": "^7.10.4", - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" + "@babel/traverse": "^7.12.5", + "@babel/types": "^7.12.5" }, "dependencies": { "@babel/code-frame": { @@ -1300,12 +1189,12 @@ } }, "@babel/generator": { - "version": "7.11.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.4.tgz", - "integrity": "sha512-Rn26vueFx0eOoz7iifCN2UHT6rGtnkSGWSoDRIy8jZN3B91PzeSULbswfLoOWuTuAcNwpG/mxy+uCTDnZ9Mp1g==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.5.tgz", + "integrity": "sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A==", "dev": true, "requires": { - "@babel/types": "^7.11.0", + "@babel/types": "^7.12.5", "jsesc": "^2.5.1", "source-map": "^0.5.0" } @@ -1357,9 +1246,9 @@ } }, "@babel/parser": { - "version": "7.11.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.4.tgz", - "integrity": "sha512-MggwidiH+E9j5Sh8pbrX5sJvMcsqS5o+7iB42M9/k0CD63MjYbdP4nhSh7uB5wnv2/RVzTZFTxzF/kIa5mrCqA==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.5.tgz", + "integrity": "sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ==", "dev": true }, "@babel/template": { @@ -1374,38 +1263,32 @@ } }, "@babel/traverse": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.0.tgz", - "integrity": "sha512-ZB2V+LskoWKNpMq6E5UUCrjtDUh5IOTAyIl0dTjIEoXum/iKWkoIEKIRDnUucO6f+2FzNkE0oD4RLKoPIufDtg==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.5.tgz", + "integrity": "sha512-xa15FbQnias7z9a62LwYAA5SZZPkHIXpd42C6uW68o8uTuua96FHZy1y61Va5P/i83FAAcMpW8+A/QayntzuqA==", "dev": true, "requires": { "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.11.0", + "@babel/generator": "^7.12.5", "@babel/helper-function-name": "^7.10.4", "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/parser": "^7.11.0", - "@babel/types": "^7.11.0", + "@babel/parser": "^7.12.5", + "@babel/types": "^7.12.5", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.19" } }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", "lodash": "^4.17.19", "to-fast-properties": "^2.0.0" } - }, - "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", - "dev": true } } }, @@ -1427,13 +1310,13 @@ "dev": true }, "@babel/plugin-proposal-async-generator-functions": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.5.tgz", - "integrity": "sha512-cNMCVezQbrRGvXJwm9fu/1sJj9bHdGAgKodZdLqOQIpfoH3raqmRPBM17+lh7CzhiKRRBrGtZL9WcjxSoGYUSg==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.12.1.tgz", + "integrity": "sha512-d+/o30tJxFxrA1lhzJqiUcEJdI6jKlNregCv5bASeGf2Q4MXmnwH7viDo7nhx1/ohf09oaH8j1GVYG/e3Yqk6A==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-remap-async-to-generator": "^7.10.4", + "@babel/helper-remap-async-to-generator": "^7.12.1", "@babel/plugin-syntax-async-generators": "^7.8.0" }, "dependencies": { @@ -1446,11 +1329,11 @@ } }, "@babel/plugin-proposal-class-properties": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.10.4.tgz", - "integrity": "sha512-vhwkEROxzcHGNu2mzUC0OFFNXdZ4M23ib8aRRcJSsW8BZK9pQMD7QB7csl97NBbgGZO7ZyHUyKDnxzOaP4IrCg==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.12.1.tgz", + "integrity": "sha512-cKp3dlQsFsEs5CWKnN7BnSHOd0EOW8EKpEjkoz1pO2E5KzIDNV9Ros1b0CnmbVgAGXJubOYVBOGCT1OmJwOI7w==", "requires": { - "@babel/helper-create-class-features-plugin": "^7.10.4", + "@babel/helper-create-class-features-plugin": "^7.12.1", "@babel/helper-plugin-utils": "^7.10.4" }, "dependencies": { @@ -1462,9 +1345,9 @@ } }, "@babel/plugin-proposal-dynamic-import": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.10.4.tgz", - "integrity": "sha512-up6oID1LeidOOASNXgv/CFbgBqTuKJ0cJjz6An5tWD+NVBNlp3VNSBxv2ZdU7SYl3NxJC7agAQDApZusV6uFwQ==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.12.1.tgz", + "integrity": "sha512-a4rhUSZFuq5W8/OO8H7BL5zspjnc1FLd9hlOxIK/f7qG4a0qsqk8uvF/ywgBA8/OmjsapjpvaEOYItfGG1qIvQ==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4", @@ -1480,9 +1363,9 @@ } }, "@babel/plugin-proposal-export-namespace-from": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.10.4.tgz", - "integrity": "sha512-aNdf0LY6/3WXkhh0Fdb6Zk9j1NMD8ovj3F6r0+3j837Pn1S1PdNtcwJ5EG9WkVPNHPxyJDaxMaAOVq4eki0qbg==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.12.1.tgz", + "integrity": "sha512-6CThGf0irEkzujYS5LQcjBx8j/4aQGiVv7J9+2f7pGfxqyKh3WnmVJYW3hdrQjyksErMGBPQrCnHfOtna+WLbw==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4", @@ -1498,9 +1381,9 @@ } }, "@babel/plugin-proposal-json-strings": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.10.4.tgz", - "integrity": "sha512-fCL7QF0Jo83uy1K0P2YXrfX11tj3lkpN7l4dMv9Y9VkowkhkQDwFHFd8IiwyK5MZjE8UpbgokkgtcReH88Abaw==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.12.1.tgz", + "integrity": "sha512-GoLDUi6U9ZLzlSda2Df++VSqDJg3CG+dR0+iWsv6XRw1rEq+zwt4DirM9yrxW6XWaTpmai1cWJLMfM8qQJf+yw==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4", @@ -1516,9 +1399,9 @@ } }, "@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.11.0.tgz", - "integrity": "sha512-/f8p4z+Auz0Uaf+i8Ekf1iM7wUNLcViFUGiPxKeXvxTSl63B875YPiVdUDdem7hREcI0E0kSpEhS8tF5RphK7Q==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.12.1.tgz", + "integrity": "sha512-k8ZmVv0JU+4gcUGeCDZOGd0lCIamU/sMtIiX3UWnUc5yzgq6YUGyEolNYD+MLYKfSzgECPcqetVcJP9Afe/aCA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4", @@ -1543,9 +1426,9 @@ } }, "@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.10.4.tgz", - "integrity": "sha512-wq5n1M3ZUlHl9sqT2ok1T2/MTt6AXE0e1Lz4WzWBr95LsAZ5qDXe4KnFuauYyEyLiohvXFMdbsOTMyLZs91Zlw==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.12.1.tgz", + "integrity": "sha512-nZY0ESiaQDI1y96+jk6VxMOaL4LPo/QDHBqL+SF3/vl6dHkTwHlOI8L4ZwuRBHgakRBw5zsVylel7QPbbGuYgg==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4", @@ -1561,9 +1444,9 @@ } }, "@babel/plugin-proposal-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.10.4.tgz", - "integrity": "sha512-73/G7QoRoeNkLZFxsoCCvlg4ezE4eM+57PnOqgaPOozd5myfj7p0muD1mRVJvbUWbOzD+q3No2bWbaKy+DJ8DA==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.12.5.tgz", + "integrity": "sha512-UiAnkKuOrCyjZ3sYNHlRlfuZJbBHknMQ9VMwVeX97Ofwx7RpD6gS2HfqTCh8KNUQgcOm8IKt103oR4KIjh7Q8g==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4", @@ -1588,14 +1471,14 @@ } }, "@babel/plugin-proposal-object-rest-spread": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.11.0.tgz", - "integrity": "sha512-wzch41N4yztwoRw0ak+37wxwJM2oiIiy6huGCoqkvSTA9acYWcPfn9Y4aJqmFFJ70KTJUu29f3DQ43uJ9HXzEA==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.1.tgz", + "integrity": "sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4", "@babel/plugin-syntax-object-rest-spread": "^7.8.0", - "@babel/plugin-transform-parameters": "^7.10.4" + "@babel/plugin-transform-parameters": "^7.12.1" }, "dependencies": { "@babel/helper-plugin-utils": { @@ -1607,9 +1490,9 @@ } }, "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.10.4.tgz", - "integrity": "sha512-LflT6nPh+GK2MnFiKDyLiqSqVHkQnVf7hdoAvyTnnKj9xB3docGRsdPuxp6qqqW19ifK3xgc9U5/FwrSaCNX5g==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.12.1.tgz", + "integrity": "sha512-hFvIjgprh9mMw5v42sJWLI1lzU5L2sznP805zeT6rySVRA0Y18StRhDqhSxlap0oVgItRsB6WSROp4YnJTJz0g==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4", @@ -1625,13 +1508,13 @@ } }, "@babel/plugin-proposal-optional-chaining": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.11.0.tgz", - "integrity": "sha512-v9fZIu3Y8562RRwhm1BbMRxtqZNFmFA2EG+pT2diuU8PT3H6T/KXoZ54KgYisfOFZHV6PfvAiBIZ9Rcz+/JCxA==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.12.1.tgz", + "integrity": "sha512-c2uRpY6WzaVDzynVY9liyykS+kVU+WRZPMPYpkelXH8KBt1oXoI89kPbZKKG/jDT5UK92FTW2fZkZaJhdiBabw==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-skip-transparent-expression-wrappers": "^7.11.0", + "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1", "@babel/plugin-syntax-optional-chaining": "^7.8.0" }, "dependencies": { @@ -1644,12 +1527,12 @@ } }, "@babel/plugin-proposal-private-methods": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.10.4.tgz", - "integrity": "sha512-wh5GJleuI8k3emgTg5KkJK6kHNsGEr0uBTDBuQUBJwckk9xs1ez79ioheEVVxMLyPscB0LfkbVHslQqIzWV6Bw==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.12.1.tgz", + "integrity": "sha512-mwZ1phvH7/NHK6Kf8LP7MYDogGV+DKB1mryFOEwx5EBNQrosvIczzZFTUmWaeujd5xT6G1ELYWUz3CutMhjE1w==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.10.4", + "@babel/helper-create-class-features-plugin": "^7.12.1", "@babel/helper-plugin-utils": "^7.10.4" }, "dependencies": { @@ -1662,12 +1545,12 @@ } }, "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.10.4.tgz", - "integrity": "sha512-H+3fOgPnEXFL9zGYtKQe4IDOPKYlZdF1kqFDQRRb8PK4B8af1vAGK04tF5iQAAsui+mHNBQSAtd2/ndEDe9wuA==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.12.1.tgz", + "integrity": "sha512-MYq+l+PvHuw/rKUz1at/vb6nCnQ2gmJBNaM62z0OgH7B2W1D9pvkpYtlti9bGtizNIU1K3zm4bZF9F91efVY0w==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.10.4", + "@babel/helper-create-regexp-features-plugin": "^7.12.1", "@babel/helper-plugin-utils": "^7.10.4" }, "dependencies": { @@ -1734,9 +1617,9 @@ } }, "@babel/plugin-syntax-jsx": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.10.4.tgz", - "integrity": "sha512-KCg9mio9jwiARCB7WAcQ7Y1q+qicILjoK8LP/VkPkEKaf5dkaZZK1EcTe91a3JJlZ3qy6L5s9X52boEYi8DM9g==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.1.tgz", + "integrity": "sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" @@ -1805,9 +1688,9 @@ } }, "@babel/plugin-syntax-top-level-await": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.10.4.tgz", - "integrity": "sha512-ni1brg4lXEmWyafKr0ccFWkJG0CeMt4WV1oyeBW6EFObF4oOHclbkj5cARxAPQyAQ2UTuplJyK4nfkXIMMFvsQ==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.1.tgz", + "integrity": "sha512-i7ooMZFS+a/Om0crxZodrTzNEPJHZrlMVGMTEpFAj6rYY/bKCddB0Dk/YxfPuYXOopuhKk/e1jV6h+WUU9XN3A==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" @@ -1822,9 +1705,9 @@ } }, "@babel/plugin-syntax-typescript": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.10.4.tgz", - "integrity": "sha512-oSAEz1YkBCAKr5Yiq8/BNtvSAPwkp/IyUnwZogd8p+F0RuYQQrLeRUzIQhueQTTBy/F+a40uS7OFKxnkRvmvFQ==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.12.1.tgz", + "integrity": "sha512-UZNEcCY+4Dp9yYRCAHrHDU+9ZXLYaY9MgBXSRLkB9WjYFRR6quJBumfVrEkUxrePPBwFcpWfNKXqVRQQtm7mMA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" @@ -1839,9 +1722,9 @@ } }, "@babel/plugin-transform-arrow-functions": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.10.4.tgz", - "integrity": "sha512-9J/oD1jV0ZCBcgnoFWFq1vJd4msoKb/TCpGNFyyLt0zABdcvgK3aYikZ8HjzB14c26bc7E3Q1yugpwGy2aTPNA==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.12.1.tgz", + "integrity": "sha512-5QB50qyN44fzzz4/qxDPQMBCTHgxg3n0xRBLJUmBlLoU/sFvxVWGZF/ZUfMVDQuJUKXaBhbupxIzIfZ6Fwk/0A==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" @@ -1856,14 +1739,14 @@ } }, "@babel/plugin-transform-async-to-generator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.10.4.tgz", - "integrity": "sha512-F6nREOan7J5UXTLsDsZG3DXmZSVofr2tGNwfdrVwkDWHfQckbQXnXSPfD7iO+c/2HGqycwyLST3DnZ16n+cBJQ==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.12.1.tgz", + "integrity": "sha512-SDtqoEcarK1DFlRJ1hHRY5HvJUj5kX4qmtpMAm2QnhOlyuMC4TMdCRgW6WXpv93rZeYNeLP22y8Aq2dbcDRM1A==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.10.4", + "@babel/helper-module-imports": "^7.12.1", "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-remap-async-to-generator": "^7.10.4" + "@babel/helper-remap-async-to-generator": "^7.12.1" }, "dependencies": { "@babel/helper-plugin-utils": { @@ -1875,9 +1758,9 @@ } }, "@babel/plugin-transform-block-scoped-functions": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.10.4.tgz", - "integrity": "sha512-WzXDarQXYYfjaV1szJvN3AD7rZgZzC1JtjJZ8dMHUyiK8mxPRahynp14zzNjU3VkPqPsO38CzxiWO1c9ARZ8JA==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.12.1.tgz", + "integrity": "sha512-5OpxfuYnSgPalRpo8EWGPzIYf0lHBWORCkj5M0oLBwHdlux9Ri36QqGW3/LR13RSVOAoUUMzoPI/jpE4ABcHoA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" @@ -1892,9 +1775,9 @@ } }, "@babel/plugin-transform-block-scoping": { - "version": "7.11.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.11.1.tgz", - "integrity": "sha512-00dYeDE0EVEHuuM+26+0w/SCL0BH2Qy7LwHuI4Hi4MH5gkC8/AqMN5uWFJIsoXZrAphiMm1iXzBw6L2T+eA0ew==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.1.tgz", + "integrity": "sha512-zJyAC9sZdE60r1nVQHblcfCj29Dh2Y0DOvlMkcqSo0ckqjiCwNiUezUKw+RjOCwGfpLRwnAeQ2XlLpsnGkvv9w==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" @@ -1909,9 +1792,9 @@ } }, "@babel/plugin-transform-classes": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.4.tgz", - "integrity": "sha512-2oZ9qLjt161dn1ZE0Ms66xBncQH4In8Sqw1YWgBUZuGVJJS5c0OFZXL6dP2MRHrkU/eKhWg8CzFJhRQl50rQxA==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.12.1.tgz", + "integrity": "sha512-/74xkA7bVdzQTBeSUhLLJgYIcxw/dpEpCdRDiHgPJ3Mv6uC11UhjpOhl72CgqbBCmt1qtssCyB2xnJm1+PFjog==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.10.4", @@ -1919,7 +1802,7 @@ "@babel/helper-function-name": "^7.10.4", "@babel/helper-optimise-call-expression": "^7.10.4", "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-replace-supers": "^7.10.4", + "@babel/helper-replace-supers": "^7.12.1", "@babel/helper-split-export-declaration": "^7.10.4", "globals": "^11.1.0" }, @@ -1986,9 +1869,9 @@ } }, "@babel/parser": { - "version": "7.11.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.4.tgz", - "integrity": "sha512-MggwidiH+E9j5Sh8pbrX5sJvMcsqS5o+7iB42M9/k0CD63MjYbdP4nhSh7uB5wnv2/RVzTZFTxzF/kIa5mrCqA==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.5.tgz", + "integrity": "sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ==", "dev": true }, "@babel/template": { @@ -2003,28 +1886,22 @@ } }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", "lodash": "^4.17.19", "to-fast-properties": "^2.0.0" } - }, - "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", - "dev": true } } }, "@babel/plugin-transform-computed-properties": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.10.4.tgz", - "integrity": "sha512-JFwVDXcP/hM/TbyzGq3l/XWGut7p46Z3QvqFMXTfk6/09m7xZHJUN9xHfsv7vqqD4YnfI5ueYdSJtXqqBLyjBw==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.12.1.tgz", + "integrity": "sha512-vVUOYpPWB7BkgUWPo4C44mUQHpTZXakEqFjbv8rQMg7TC6S6ZhGZ3otQcRH6u7+adSlE5i0sp63eMC/XGffrzg==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" @@ -2039,9 +1916,9 @@ } }, "@babel/plugin-transform-destructuring": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.10.4.tgz", - "integrity": "sha512-+WmfvyfsyF603iPa6825mq6Qrb7uLjTOsa3XOFzlYcYDHSS4QmpOWOL0NNBY5qMbvrcf3tq0Cw+v4lxswOBpgA==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.12.1.tgz", + "integrity": "sha512-fRMYFKuzi/rSiYb2uRLiUENJOKq4Gnl+6qOv5f8z0TZXg3llUwUhsNNwrwaT/6dUhJTzNpBr+CUvEWBtfNY1cw==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" @@ -2056,12 +1933,12 @@ } }, "@babel/plugin-transform-dotall-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.10.4.tgz", - "integrity": "sha512-ZEAVvUTCMlMFAbASYSVQoxIbHm2OkG2MseW6bV2JjIygOjdVv8tuxrCTzj1+Rynh7ODb8GivUy7dzEXzEhuPaA==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.12.1.tgz", + "integrity": "sha512-B2pXeRKoLszfEW7J4Hg9LoFaWEbr/kzo3teWHmtFCszjRNa/b40f9mfeqZsIDLLt/FjwQ6pz/Gdlwy85xNckBA==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.10.4", + "@babel/helper-create-regexp-features-plugin": "^7.12.1", "@babel/helper-plugin-utils": "^7.10.4" }, "dependencies": { @@ -2074,9 +1951,9 @@ } }, "@babel/plugin-transform-duplicate-keys": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.10.4.tgz", - "integrity": "sha512-GL0/fJnmgMclHiBTTWXNlYjYsA7rDrtsazHG6mglaGSTh0KsrW04qml+Bbz9FL0LcJIRwBWL5ZqlNHKTkU3xAA==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.12.1.tgz", + "integrity": "sha512-iRght0T0HztAb/CazveUpUQrZY+aGKKaWXMJ4uf9YJtqxSUe09j3wteztCUDRHs+SRAL7yMuFqUsLoAKKzgXjw==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" @@ -2091,9 +1968,9 @@ } }, "@babel/plugin-transform-exponentiation-operator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.10.4.tgz", - "integrity": "sha512-S5HgLVgkBcRdyQAHbKj+7KyuWx8C6t5oETmUuwz1pt3WTWJhsUV0WIIXuVvfXMxl/QQyHKlSCNNtaIamG8fysw==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.12.1.tgz", + "integrity": "sha512-7tqwy2bv48q+c1EHbXK0Zx3KXd2RVQp6OC7PbwFNt/dPTAV3Lu5sWtWuAj8owr5wqtWnqHfl2/mJlUmqkChKug==", "dev": true, "requires": { "@babel/helper-builder-binary-assignment-operator-visitor": "^7.10.4", @@ -2109,9 +1986,9 @@ } }, "@babel/plugin-transform-for-of": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.10.4.tgz", - "integrity": "sha512-ItdQfAzu9AlEqmusA/65TqJ79eRcgGmpPPFvBnGILXZH975G0LNjP1yjHvGgfuCxqrPPueXOPe+FsvxmxKiHHQ==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.12.1.tgz", + "integrity": "sha512-Zaeq10naAsuHo7heQvyV0ptj4dlZJwZgNAtBYBnu5nNKJoW62m0zKcIEyVECrUKErkUkg6ajMy4ZfnVZciSBhg==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" @@ -2126,9 +2003,9 @@ } }, "@babel/plugin-transform-function-name": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.10.4.tgz", - "integrity": "sha512-OcDCq2y5+E0dVD5MagT5X+yTRbcvFjDI2ZVAottGH6tzqjx/LKpgkUepu3hp/u4tZBzxxpNGwLsAvGBvQ2mJzg==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.12.1.tgz", + "integrity": "sha512-JF3UgJUILoFrFMEnOJLJkRHSk6LUSXLmEFsA23aR2O5CSLUxbeUX1IZ1YQ7Sn0aXb601Ncwjx73a+FVqgcljVw==", "dev": true, "requires": { "@babel/helper-function-name": "^7.10.4", @@ -2188,9 +2065,9 @@ } }, "@babel/parser": { - "version": "7.11.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.4.tgz", - "integrity": "sha512-MggwidiH+E9j5Sh8pbrX5sJvMcsqS5o+7iB42M9/k0CD63MjYbdP4nhSh7uB5wnv2/RVzTZFTxzF/kIa5mrCqA==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.5.tgz", + "integrity": "sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ==", "dev": true }, "@babel/template": { @@ -2205,28 +2082,22 @@ } }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", "lodash": "^4.17.19", "to-fast-properties": "^2.0.0" } - }, - "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", - "dev": true } } }, "@babel/plugin-transform-literals": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.10.4.tgz", - "integrity": "sha512-Xd/dFSTEVuUWnyZiMu76/InZxLTYilOSr1UlHV+p115Z/Le2Fi1KXkJUYz0b42DfndostYlPub3m8ZTQlMaiqQ==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.12.1.tgz", + "integrity": "sha512-+PxVGA+2Ag6uGgL0A5f+9rklOnnMccwEBzwYFL3EUaKuiyVnUipyXncFcfjSkbimLrODoqki1U9XxZzTvfN7IQ==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" @@ -2241,9 +2112,9 @@ } }, "@babel/plugin-transform-member-expression-literals": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.10.4.tgz", - "integrity": "sha512-0bFOvPyAoTBhtcJLr9VcwZqKmSjFml1iVxvPL0ReomGU53CX53HsM4h2SzckNdkQcHox1bpAqzxBI1Y09LlBSw==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.12.1.tgz", + "integrity": "sha512-1sxePl6z9ad0gFMB9KqmYofk34flq62aqMt9NqliS/7hPEpURUCMbyHXrMPlo282iY7nAvUB1aQd5mg79UD9Jg==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" @@ -2258,12 +2129,12 @@ } }, "@babel/plugin-transform-modules-amd": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.10.5.tgz", - "integrity": "sha512-elm5uruNio7CTLFItVC/rIzKLfQ17+fX7EVz5W0TMgIHFo1zY0Ozzx+lgwhL4plzl8OzVn6Qasx5DeEFyoNiRw==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.12.1.tgz", + "integrity": "sha512-tDW8hMkzad5oDtzsB70HIQQRBiTKrhfgwC/KkJeGsaNFTdWhKNt/BiE8c5yj19XiGyrxpbkOfH87qkNg1YGlOQ==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.10.5", + "@babel/helper-module-transforms": "^7.12.1", "@babel/helper-plugin-utils": "^7.10.4", "babel-plugin-dynamic-import-node": "^2.3.3" }, @@ -2277,14 +2148,14 @@ } }, "@babel/plugin-transform-modules-commonjs": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.10.4.tgz", - "integrity": "sha512-Xj7Uq5o80HDLlW64rVfDBhao6OX89HKUmb+9vWYaLXBZOma4gA6tw4Ni1O5qVDoZWUV0fxMYA0aYzOawz0l+1w==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.12.1.tgz", + "integrity": "sha512-dY789wq6l0uLY8py9c1B48V8mVL5gZh/+PQ5ZPrylPYsnAvnEMjqsUXkuoDVPeVK+0VyGar+D08107LzDQ6pag==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.10.4", + "@babel/helper-module-transforms": "^7.12.1", "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-simple-access": "^7.10.4", + "@babel/helper-simple-access": "^7.12.1", "babel-plugin-dynamic-import-node": "^2.3.3" }, "dependencies": { @@ -2297,14 +2168,15 @@ } }, "@babel/plugin-transform-modules-systemjs": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.10.5.tgz", - "integrity": "sha512-f4RLO/OL14/FP1AEbcsWMzpbUz6tssRaeQg11RH1BP/XnPpRoVwgeYViMFacnkaw4k4wjRSjn3ip1Uw9TaXuMw==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.12.1.tgz", + "integrity": "sha512-Hn7cVvOavVh8yvW6fLwveFqSnd7rbQN3zJvoPNyNaQSvgfKmDBO9U1YL9+PCXGRlZD9tNdWTy5ACKqMuzyn32Q==", "dev": true, "requires": { "@babel/helper-hoist-variables": "^7.10.4", - "@babel/helper-module-transforms": "^7.10.5", + "@babel/helper-module-transforms": "^7.12.1", "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-validator-identifier": "^7.10.4", "babel-plugin-dynamic-import-node": "^2.3.3" }, "dependencies": { @@ -2313,16 +2185,22 @@ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", "dev": true + }, + "@babel/helper-validator-identifier": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", + "dev": true } } }, "@babel/plugin-transform-modules-umd": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.10.4.tgz", - "integrity": "sha512-mohW5q3uAEt8T45YT7Qc5ws6mWgJAaL/8BfWD9Dodo1A3RKWli8wTS+WiQ/knF+tXlPirW/1/MqzzGfCExKECA==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.12.1.tgz", + "integrity": "sha512-aEIubCS0KHKM0zUos5fIoQm+AZUMt1ZvMpqz0/H5qAQ7vWylr9+PLYurT+Ic7ID/bKLd4q8hDovaG3Zch2uz5Q==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.10.4", + "@babel/helper-module-transforms": "^7.12.1", "@babel/helper-plugin-utils": "^7.10.4" }, "dependencies": { @@ -2335,18 +2213,18 @@ } }, "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.10.4.tgz", - "integrity": "sha512-V6LuOnD31kTkxQPhKiVYzYC/Jgdq53irJC/xBSmqcNcqFGV+PER4l6rU5SH2Vl7bH9mLDHcc0+l9HUOe4RNGKA==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.12.1.tgz", + "integrity": "sha512-tB43uQ62RHcoDp9v2Nsf+dSM8sbNodbEicbQNA53zHz8pWUhsgHSJCGpt7daXxRydjb0KnfmB+ChXOv3oADp1Q==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.10.4" + "@babel/helper-create-regexp-features-plugin": "^7.12.1" } }, "@babel/plugin-transform-new-target": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.10.4.tgz", - "integrity": "sha512-YXwWUDAH/J6dlfwqlWsztI2Puz1NtUAubXhOPLQ5gjR/qmQ5U96DY4FQO8At33JN4XPBhrjB8I4eMmLROjjLjw==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.12.1.tgz", + "integrity": "sha512-+eW/VLcUL5L9IvJH7rT1sT0CzkdUTvPrXC2PXTn/7z7tXLBuKvezYbGdxD5WMRoyvyaujOq2fWoKl869heKjhw==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" @@ -2361,13 +2239,13 @@ } }, "@babel/plugin-transform-object-super": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.10.4.tgz", - "integrity": "sha512-5iTw0JkdRdJvr7sY0vHqTpnruUpTea32JHmq/atIWqsnNussbRzjEDyWep8UNztt1B5IusBYg8Irb0bLbiEBCQ==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.12.1.tgz", + "integrity": "sha512-AvypiGJH9hsquNUn+RXVcBdeE3KHPZexWRdimhuV59cSoOt5kFBmqlByorAeUlGG2CJWd0U+4ZtNKga/TB0cAw==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-replace-supers": "^7.10.4" + "@babel/helper-replace-supers": "^7.12.1" }, "dependencies": { "@babel/helper-plugin-utils": { @@ -2379,59 +2257,26 @@ } }, "@babel/plugin-transform-parameters": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.10.5.tgz", - "integrity": "sha512-xPHwUj5RdFV8l1wuYiu5S9fqWGM2DrYc24TMvUiRrPVm+SM3XeqU9BcokQX/kEUe+p2RBwy+yoiR1w/Blq6ubw==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.12.1.tgz", + "integrity": "sha512-xq9C5EQhdPK23ZeCdMxl8bbRnAgHFrw5EOC3KJUsSylZqdkCaFEXxGSBuTSObOpiiHHNyb82es8M1QYgfQGfNg==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.10.4", "@babel/helper-plugin-utils": "^7.10.4" }, "dependencies": { - "@babel/helper-get-function-arity": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", - "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, "@babel/helper-plugin-utils": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", "dev": true - }, - "@babel/helper-validator-identifier": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", - "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", - "dev": true - }, - "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" - } - }, - "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", - "dev": true } } }, "@babel/plugin-transform-property-literals": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.10.4.tgz", - "integrity": "sha512-ofsAcKiUxQ8TY4sScgsGeR2vJIsfrzqvFb9GvJ5UdXDzl+MyYCaBj/FGzXuv7qE0aJcjWMILny1epqelnFlz8g==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.12.1.tgz", + "integrity": "sha512-6MTCR/mZ1MQS+AwZLplX4cEySjCpnIF26ToWo942nqn8hXSm7McaHQNeGx/pt7suI1TWOWMfa/NgBhiqSnX0cQ==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" @@ -2446,9 +2291,9 @@ } }, "@babel/plugin-transform-react-constant-elements": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.10.4.tgz", - "integrity": "sha512-cYmQBW1pXrqBte1raMkAulXmi7rjg3VI6ZLg9QIic8Hq7BtYXaWuZSxsr2siOMI6SWwpxjWfnwhTUrd7JlAV7g==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.12.1.tgz", + "integrity": "sha512-KOHd0tIRLoER+J+8f9DblZDa1fLGPwaaN1DI1TVHuQFOpjHV22C3CUB3obeC4fexHY9nx+fH0hQNvLFFfA1mxA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" @@ -2463,9 +2308,9 @@ } }, "@babel/plugin-transform-react-display-name": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.10.4.tgz", - "integrity": "sha512-Zd4X54Mu9SBfPGnEcaGcOrVAYOtjT2on8QZkLKEq1S/tHexG39d9XXGZv19VfRrDjPJzFmPfTAqOQS1pfFOujw==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.12.1.tgz", + "integrity": "sha512-cAzB+UzBIrekfYxyLlFqf/OagTvHLcVBb5vpouzkYkBclRPraiygVnafvAoipErZLI8ANv8Ecn6E/m5qPXD26w==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" @@ -2480,15 +2325,15 @@ } }, "@babel/plugin-transform-react-jsx": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.10.4.tgz", - "integrity": "sha512-L+MfRhWjX0eI7Js093MM6MacKU4M6dnCRa/QPDwYMxjljzSCzzlzKzj9Pk4P3OtrPcxr2N3znR419nr3Xw+65A==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.12.5.tgz", + "integrity": "sha512-2xkcPqqrYiOQgSlM/iwto1paPijjsDbUynN13tI6bosDz/jOW3CRzYguIE8wKX32h+msbBM22Dv5fwrFkUOZjQ==", "dev": true, "requires": { "@babel/helper-builder-react-jsx": "^7.10.4", - "@babel/helper-builder-react-jsx-experimental": "^7.10.4", + "@babel/helper-builder-react-jsx-experimental": "^7.12.1", "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-jsx": "^7.10.4" + "@babel/plugin-syntax-jsx": "^7.12.1" }, "dependencies": { "@babel/helper-plugin-utils": { @@ -2500,14 +2345,14 @@ } }, "@babel/plugin-transform-react-jsx-development": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.10.4.tgz", - "integrity": "sha512-RM3ZAd1sU1iQ7rI2dhrZRZGv0aqzNQMbkIUCS1txYpi9wHQ2ZHNjo5TwX+UD6pvFW4AbWqLVYvKy5qJSAyRGjQ==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.12.5.tgz", + "integrity": "sha512-1JJusg3iPgsZDthyWiCr3KQiGs31ikU/mSf2N2dSYEAO0GEImmVUbWf0VoSDGDFTAn5Dj4DUiR6SdIXHY7tELA==", "dev": true, "requires": { - "@babel/helper-builder-react-jsx-experimental": "^7.10.4", + "@babel/helper-builder-react-jsx-experimental": "^7.12.1", "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-jsx": "^7.10.4" + "@babel/plugin-syntax-jsx": "^7.12.1" }, "dependencies": { "@babel/helper-plugin-utils": { @@ -2519,13 +2364,12 @@ } }, "@babel/plugin-transform-react-jsx-self": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.10.4.tgz", - "integrity": "sha512-yOvxY2pDiVJi0axdTWHSMi5T0DILN+H+SaeJeACHKjQLezEzhLx9nEF9xgpBLPtkZsks9cnb5P9iBEi21En3gg==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.12.1.tgz", + "integrity": "sha512-FbpL0ieNWiiBB5tCldX17EtXgmzeEZjFrix72rQYeq9X6nUK38HCaxexzVQrZWXanxKJPKVVIU37gFjEQYkPkA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-jsx": "^7.10.4" + "@babel/helper-plugin-utils": "^7.10.4" }, "dependencies": { "@babel/helper-plugin-utils": { @@ -2537,13 +2381,12 @@ } }, "@babel/plugin-transform-react-jsx-source": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.10.5.tgz", - "integrity": "sha512-wTeqHVkN1lfPLubRiZH3o73f4rfon42HpgxUSs86Nc+8QIcm/B9s8NNVXu/gwGcOyd7yDib9ikxoDLxJP0UiDA==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.12.1.tgz", + "integrity": "sha512-keQ5kBfjJNRc6zZN1/nVHCd6LLIHq4aUKcVnvE/2l+ZZROSbqoiGFRtT5t3Is89XJxBQaP7NLZX2jgGHdZvvFQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-jsx": "^7.10.4" + "@babel/helper-plugin-utils": "^7.10.4" }, "dependencies": { "@babel/helper-plugin-utils": { @@ -2555,9 +2398,9 @@ } }, "@babel/plugin-transform-react-pure-annotations": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.10.4.tgz", - "integrity": "sha512-+njZkqcOuS8RaPakrnR9KvxjoG1ASJWpoIv/doyWngId88JoFlPlISenGXjrVacZUIALGUr6eodRs1vmPnF23A==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.12.1.tgz", + "integrity": "sha512-RqeaHiwZtphSIUZ5I85PEH19LOSzxfuEazoY7/pWASCAIBuATQzpSVD+eT6MebeeZT2F4eSL0u4vw6n4Nm0Mjg==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.10.4", @@ -2573,18 +2416,18 @@ } }, "@babel/plugin-transform-regenerator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.10.4.tgz", - "integrity": "sha512-3thAHwtor39A7C04XucbMg17RcZ3Qppfxr22wYzZNcVIkPHfpM9J0SO8zuCV6SZa265kxBJSrfKTvDCYqBFXGw==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.12.1.tgz", + "integrity": "sha512-gYrHqs5itw6i4PflFX3OdBPMQdPbF4bj2REIUxlMRUFk0/ZOAIpDFuViuxPjUL7YC8UPnf+XG7/utJvqXdPKng==", "dev": true, "requires": { "regenerator-transform": "^0.14.2" } }, "@babel/plugin-transform-reserved-words": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.10.4.tgz", - "integrity": "sha512-hGsw1O6Rew1fkFbDImZIEqA8GoidwTAilwCyWqLBM9f+e/u/sQMQu7uX6dyokfOayRuuVfKOW4O7HvaBWM+JlQ==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.12.1.tgz", + "integrity": "sha512-pOnUfhyPKvZpVyBHhSBoX8vfA09b7r00Pmm1sH+29ae2hMTKVmSp4Ztsr8KBKjLjx17H0eJqaRC3bR2iThM54A==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" @@ -2599,9 +2442,9 @@ } }, "@babel/plugin-transform-shorthand-properties": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.10.4.tgz", - "integrity": "sha512-AC2K/t7o07KeTIxMoHneyX90v3zkm5cjHJEokrPEAGEy3UCp8sLKfnfOIGdZ194fyN4wfX/zZUWT9trJZ0qc+Q==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.12.1.tgz", + "integrity": "sha512-GFZS3c/MhX1OusqB1MZ1ct2xRzX5ppQh2JU1h2Pnfk88HtFTM+TWQqJNfwkmxtPQtb/s1tk87oENfXJlx7rSDw==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" @@ -2616,13 +2459,13 @@ } }, "@babel/plugin-transform-spread": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.11.0.tgz", - "integrity": "sha512-UwQYGOqIdQJe4aWNyS7noqAnN2VbaczPLiEtln+zPowRNlD+79w3oi2TWfYe0eZgd+gjZCbsydN7lzWysDt+gw==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.12.1.tgz", + "integrity": "sha512-vuLp8CP0BE18zVYjsEBZ5xoCecMK6LBMMxYzJnh01rxQRvhNhH1csMMmBfNo5tGpGO+NhdSNW2mzIvBu3K1fng==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-skip-transparent-expression-wrappers": "^7.11.0" + "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1" }, "dependencies": { "@babel/helper-plugin-utils": { @@ -2634,9 +2477,9 @@ } }, "@babel/plugin-transform-sticky-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.10.4.tgz", - "integrity": "sha512-Ddy3QZfIbEV0VYcVtFDCjeE4xwVTJWTmUtorAJkn6u/92Z/nWJNV+mILyqHKrUxXYKA2EoCilgoPePymKL4DvQ==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.12.1.tgz", + "integrity": "sha512-CiUgKQ3AGVk7kveIaPEET1jNDhZZEl1RPMWdTBE1799bdz++SwqDHStmxfCtDfBhQgCl38YRiSnrMuUMZIWSUQ==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4", @@ -2652,12 +2495,11 @@ } }, "@babel/plugin-transform-template-literals": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.10.5.tgz", - "integrity": "sha512-V/lnPGIb+KT12OQikDvgSuesRX14ck5FfJXt6+tXhdkJ+Vsd0lDCVtF6jcB4rNClYFzaB2jusZ+lNISDk2mMMw==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.12.1.tgz", + "integrity": "sha512-b4Zx3KHi+taXB1dVRBhVJtEPi9h1THCeKmae2qP0YdUHIFhVjtpqqNfxeVAa1xeHVhAy4SbHxEwx5cltAu5apw==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", "@babel/helper-plugin-utils": "^7.10.4" }, "dependencies": { @@ -2670,9 +2512,9 @@ } }, "@babel/plugin-transform-typeof-symbol": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.10.4.tgz", - "integrity": "sha512-QqNgYwuuW0y0H+kUE/GWSR45t/ccRhe14Fs/4ZRouNNQsyd4o3PG4OtHiIrepbM2WKUBDAXKCAK/Lk4VhzTaGA==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.1.tgz", + "integrity": "sha512-EPGgpGy+O5Kg5pJFNDKuxt9RdmTgj5sgrus2XVeMp/ZIbOESadgILUbm50SNpghOh3/6yrbsH+NB5+WJTmsA7Q==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" @@ -2687,14 +2529,14 @@ } }, "@babel/plugin-transform-typescript": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.11.0.tgz", - "integrity": "sha512-edJsNzTtvb3MaXQwj8403B7mZoGu9ElDJQZOKjGUnvilquxBA3IQoEIOvkX/1O8xfAsnHS/oQhe2w/IXrr+w0w==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.12.1.tgz", + "integrity": "sha512-VrsBByqAIntM+EYMqSm59SiMEf7qkmI9dqMt6RbD/wlwueWmYcI0FFK5Fj47pP6DRZm+3teXjosKlwcZJ5lIMw==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.10.5", + "@babel/helper-create-class-features-plugin": "^7.12.1", "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-typescript": "^7.10.4" + "@babel/plugin-syntax-typescript": "^7.12.1" }, "dependencies": { "@babel/helper-plugin-utils": { @@ -2706,9 +2548,9 @@ } }, "@babel/plugin-transform-unicode-escapes": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.10.4.tgz", - "integrity": "sha512-y5XJ9waMti2J+e7ij20e+aH+fho7Wb7W8rNuu72aKRwCHFqQdhkdU2lo3uZ9tQuboEJcUFayXdARhcxLQ3+6Fg==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.1.tgz", + "integrity": "sha512-I8gNHJLIc7GdApm7wkVnStWssPNbSRMPtgHdmH3sRM1zopz09UWPS4x5V4n1yz/MIWTVnJ9sp6IkuXdWM4w+2Q==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" @@ -2723,12 +2565,12 @@ } }, "@babel/plugin-transform-unicode-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.10.4.tgz", - "integrity": "sha512-wNfsc4s8N2qnIwpO/WP2ZiSyjfpTamT2C9V9FDH/Ljub9zw6P3SjkXcFmc0RQUt96k2fmIvtla2MMjgTwIAC+A==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.12.1.tgz", + "integrity": "sha512-SqH4ClNngh/zGwHZOOQMTD+e8FGWexILV+ePMyiDJttAWRh5dhDL8rcl5lSgU3Huiq6Zn6pWTMvdPAb21Dwdyg==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.10.4", + "@babel/helper-create-regexp-features-plugin": "^7.12.1", "@babel/helper-plugin-utils": "^7.10.4" }, "dependencies": { @@ -2741,30 +2583,31 @@ } }, "@babel/preset-env": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.11.0.tgz", - "integrity": "sha512-2u1/k7rG/gTh02dylX2kL3S0IJNF+J6bfDSp4DI2Ma8QN6Y9x9pmAax59fsCk6QUQG0yqH47yJWA+u1I1LccAg==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.12.1.tgz", + "integrity": "sha512-H8kxXmtPaAGT7TyBvSSkoSTUK6RHh61So05SyEbpmr0MCZrsNYn7mGMzzeYoOUCdHzww61k8XBft2TaES+xPLg==", "dev": true, "requires": { - "@babel/compat-data": "^7.11.0", - "@babel/helper-compilation-targets": "^7.10.4", - "@babel/helper-module-imports": "^7.10.4", + "@babel/compat-data": "^7.12.1", + "@babel/helper-compilation-targets": "^7.12.1", + "@babel/helper-module-imports": "^7.12.1", "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-proposal-async-generator-functions": "^7.10.4", - "@babel/plugin-proposal-class-properties": "^7.10.4", - "@babel/plugin-proposal-dynamic-import": "^7.10.4", - "@babel/plugin-proposal-export-namespace-from": "^7.10.4", - "@babel/plugin-proposal-json-strings": "^7.10.4", - "@babel/plugin-proposal-logical-assignment-operators": "^7.11.0", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.10.4", - "@babel/plugin-proposal-numeric-separator": "^7.10.4", - "@babel/plugin-proposal-object-rest-spread": "^7.11.0", - "@babel/plugin-proposal-optional-catch-binding": "^7.10.4", - "@babel/plugin-proposal-optional-chaining": "^7.11.0", - "@babel/plugin-proposal-private-methods": "^7.10.4", - "@babel/plugin-proposal-unicode-property-regex": "^7.10.4", + "@babel/helper-validator-option": "^7.12.1", + "@babel/plugin-proposal-async-generator-functions": "^7.12.1", + "@babel/plugin-proposal-class-properties": "^7.12.1", + "@babel/plugin-proposal-dynamic-import": "^7.12.1", + "@babel/plugin-proposal-export-namespace-from": "^7.12.1", + "@babel/plugin-proposal-json-strings": "^7.12.1", + "@babel/plugin-proposal-logical-assignment-operators": "^7.12.1", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.12.1", + "@babel/plugin-proposal-numeric-separator": "^7.12.1", + "@babel/plugin-proposal-object-rest-spread": "^7.12.1", + "@babel/plugin-proposal-optional-catch-binding": "^7.12.1", + "@babel/plugin-proposal-optional-chaining": "^7.12.1", + "@babel/plugin-proposal-private-methods": "^7.12.1", + "@babel/plugin-proposal-unicode-property-regex": "^7.12.1", "@babel/plugin-syntax-async-generators": "^7.8.0", - "@babel/plugin-syntax-class-properties": "^7.10.4", + "@babel/plugin-syntax-class-properties": "^7.12.1", "@babel/plugin-syntax-dynamic-import": "^7.8.0", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", "@babel/plugin-syntax-json-strings": "^7.8.0", @@ -2774,45 +2617,42 @@ "@babel/plugin-syntax-object-rest-spread": "^7.8.0", "@babel/plugin-syntax-optional-catch-binding": "^7.8.0", "@babel/plugin-syntax-optional-chaining": "^7.8.0", - "@babel/plugin-syntax-top-level-await": "^7.10.4", - "@babel/plugin-transform-arrow-functions": "^7.10.4", - "@babel/plugin-transform-async-to-generator": "^7.10.4", - "@babel/plugin-transform-block-scoped-functions": "^7.10.4", - "@babel/plugin-transform-block-scoping": "^7.10.4", - "@babel/plugin-transform-classes": "^7.10.4", - "@babel/plugin-transform-computed-properties": "^7.10.4", - "@babel/plugin-transform-destructuring": "^7.10.4", - "@babel/plugin-transform-dotall-regex": "^7.10.4", - "@babel/plugin-transform-duplicate-keys": "^7.10.4", - "@babel/plugin-transform-exponentiation-operator": "^7.10.4", - "@babel/plugin-transform-for-of": "^7.10.4", - "@babel/plugin-transform-function-name": "^7.10.4", - "@babel/plugin-transform-literals": "^7.10.4", - "@babel/plugin-transform-member-expression-literals": "^7.10.4", - "@babel/plugin-transform-modules-amd": "^7.10.4", - "@babel/plugin-transform-modules-commonjs": "^7.10.4", - "@babel/plugin-transform-modules-systemjs": "^7.10.4", - "@babel/plugin-transform-modules-umd": "^7.10.4", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.10.4", - "@babel/plugin-transform-new-target": "^7.10.4", - "@babel/plugin-transform-object-super": "^7.10.4", - "@babel/plugin-transform-parameters": "^7.10.4", - "@babel/plugin-transform-property-literals": "^7.10.4", - "@babel/plugin-transform-regenerator": "^7.10.4", - "@babel/plugin-transform-reserved-words": "^7.10.4", - "@babel/plugin-transform-shorthand-properties": "^7.10.4", - "@babel/plugin-transform-spread": "^7.11.0", - "@babel/plugin-transform-sticky-regex": "^7.10.4", - "@babel/plugin-transform-template-literals": "^7.10.4", - "@babel/plugin-transform-typeof-symbol": "^7.10.4", - "@babel/plugin-transform-unicode-escapes": "^7.10.4", - "@babel/plugin-transform-unicode-regex": "^7.10.4", + "@babel/plugin-syntax-top-level-await": "^7.12.1", + "@babel/plugin-transform-arrow-functions": "^7.12.1", + "@babel/plugin-transform-async-to-generator": "^7.12.1", + "@babel/plugin-transform-block-scoped-functions": "^7.12.1", + "@babel/plugin-transform-block-scoping": "^7.12.1", + "@babel/plugin-transform-classes": "^7.12.1", + "@babel/plugin-transform-computed-properties": "^7.12.1", + "@babel/plugin-transform-destructuring": "^7.12.1", + "@babel/plugin-transform-dotall-regex": "^7.12.1", + "@babel/plugin-transform-duplicate-keys": "^7.12.1", + "@babel/plugin-transform-exponentiation-operator": "^7.12.1", + "@babel/plugin-transform-for-of": "^7.12.1", + "@babel/plugin-transform-function-name": "^7.12.1", + "@babel/plugin-transform-literals": "^7.12.1", + "@babel/plugin-transform-member-expression-literals": "^7.12.1", + "@babel/plugin-transform-modules-amd": "^7.12.1", + "@babel/plugin-transform-modules-commonjs": "^7.12.1", + "@babel/plugin-transform-modules-systemjs": "^7.12.1", + "@babel/plugin-transform-modules-umd": "^7.12.1", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.12.1", + "@babel/plugin-transform-new-target": "^7.12.1", + "@babel/plugin-transform-object-super": "^7.12.1", + "@babel/plugin-transform-parameters": "^7.12.1", + "@babel/plugin-transform-property-literals": "^7.12.1", + "@babel/plugin-transform-regenerator": "^7.12.1", + "@babel/plugin-transform-reserved-words": "^7.12.1", + "@babel/plugin-transform-shorthand-properties": "^7.12.1", + "@babel/plugin-transform-spread": "^7.12.1", + "@babel/plugin-transform-sticky-regex": "^7.12.1", + "@babel/plugin-transform-template-literals": "^7.12.1", + "@babel/plugin-transform-typeof-symbol": "^7.12.1", + "@babel/plugin-transform-unicode-escapes": "^7.12.1", + "@babel/plugin-transform-unicode-regex": "^7.12.1", "@babel/preset-modules": "^0.1.3", - "@babel/types": "^7.11.0", - "browserslist": "^4.12.0", + "@babel/types": "^7.12.1", "core-js-compat": "^3.6.2", - "invariant": "^2.2.2", - "levenary": "^1.1.1", "semver": "^5.5.0" }, "dependencies": { @@ -2829,9 +2669,9 @@ "dev": true }, "@babel/plugin-syntax-class-properties": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.4.tgz", - "integrity": "sha512-GCSBF7iUle6rNugfURwNmCGG3Z/2+opxAMLs1nND4bhEG5PuxTIggDBoeYYSujAlLtsupzOHYJQgPS3pivwXIA==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.1.tgz", + "integrity": "sha512-U40A76x5gTwmESz+qiqssqmeEsKvcSyvtgktrm0uzcARAmM9I1jR221f6Oq+GmHrcD+LvZDag1UTOTe2fL3TeA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" @@ -2856,28 +2696,22 @@ } }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", "lodash": "^4.17.19", "to-fast-properties": "^2.0.0" } - }, - "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", - "dev": true } } }, "@babel/preset-modules": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.3.tgz", - "integrity": "sha512-Ra3JXOHBq2xd56xSF7lMKXdjBn3T772Y1Wet3yWnkDly9zHvJki029tAFzvAAK5cf4YV3yoxuP61crYRol6SVg==", + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.4.tgz", + "integrity": "sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0", @@ -2888,18 +2722,18 @@ } }, "@babel/preset-react": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.10.4.tgz", - "integrity": "sha512-BrHp4TgOIy4M19JAfO1LhycVXOPWdDbTRep7eVyatf174Hff+6Uk53sDyajqZPu8W1qXRBiYOfIamek6jA7YVw==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.12.5.tgz", + "integrity": "sha512-jcs++VPrgyFehkMezHtezS2BpnUlR7tQFAyesJn1vGTO9aTFZrgIQrA5YydlTwxbcjMwkFY6i04flCigRRr3GA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-transform-react-display-name": "^7.10.4", - "@babel/plugin-transform-react-jsx": "^7.10.4", - "@babel/plugin-transform-react-jsx-development": "^7.10.4", - "@babel/plugin-transform-react-jsx-self": "^7.10.4", - "@babel/plugin-transform-react-jsx-source": "^7.10.4", - "@babel/plugin-transform-react-pure-annotations": "^7.10.4" + "@babel/plugin-transform-react-display-name": "^7.12.1", + "@babel/plugin-transform-react-jsx": "^7.12.5", + "@babel/plugin-transform-react-jsx-development": "^7.12.5", + "@babel/plugin-transform-react-jsx-self": "^7.12.1", + "@babel/plugin-transform-react-jsx-source": "^7.12.1", + "@babel/plugin-transform-react-pure-annotations": "^7.12.1" }, "dependencies": { "@babel/helper-plugin-utils": { @@ -2911,13 +2745,13 @@ } }, "@babel/preset-typescript": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.10.4.tgz", - "integrity": "sha512-SdYnvGPv+bLlwkF2VkJnaX/ni1sMNetcGI1+nThF1gyv6Ph8Qucc4ZZAjM5yZcE/AKRXIOTZz7eSRDWOEjPyRQ==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.12.1.tgz", + "integrity": "sha512-hNK/DhmoJPsksdHuI/RVrcEws7GN5eamhi28JkO52MqIxU8Z0QpmiSOQxZHWOHV7I3P4UjHV97ay4TcamMA6Kw==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-transform-typescript": "^7.10.4" + "@babel/plugin-transform-typescript": "^7.12.1" }, "dependencies": { "@babel/helper-plugin-utils": { @@ -2929,9 +2763,9 @@ } }, "@babel/register": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.10.5.tgz", - "integrity": "sha512-eYHdLv43nyvmPn9bfNfrcC4+iYNwdQ8Pxk1MFJuU/U5LpSYl/PH4dFMazCYZDFVi8ueG3shvO+AQfLrxpYulQw==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.12.1.tgz", + "integrity": "sha512-XWcmseMIncOjoydKZnWvWi0/5CUCD+ZYKhRwgYlWOrA8fGZ/FjuLRpqtIhLOVD/fvR1b9DQHtZPn68VvhpYf+Q==", "dev": true, "requires": { "find-cache-dir": "^2.0.0", @@ -2939,14 +2773,6 @@ "make-dir": "^2.1.0", "pirates": "^4.0.0", "source-map-support": "^0.5.16" - }, - "dependencies": { - "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", - "dev": true - } } }, "@babel/runtime": { @@ -3013,15 +2839,15 @@ } }, "@hot-loader/react-dom": { - "version": "16.13.0", - "resolved": "https://registry.npmjs.org/@hot-loader/react-dom/-/react-dom-16.13.0.tgz", - "integrity": "sha512-lJZrmkucz2MrQJTQtJobx5MICXcfQvKihszqv655p557HPi0hMOWxrNpiHv3DWD8ugNWjtWcVWqRnFvwsHq1mQ==", + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/@hot-loader/react-dom/-/react-dom-16.14.0.tgz", + "integrity": "sha512-EN9czvcLsMYmSDo5yRKZOAq3ZGRlDpad1gPtX0NdMMomJXcPE3yFSeFzE94X/NjOaiSVimB7LuqPYpkWVaIi4Q==", "dev": true, "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", "prop-types": "^15.6.2", - "scheduler": "^0.19.0" + "scheduler": "^0.19.1" } }, "@istanbuljs/load-nyc-config": { @@ -3738,9 +3564,9 @@ } }, "@types/classnames": { - "version": "2.2.10", - "resolved": "https://registry.npmjs.org/@types/classnames/-/classnames-2.2.10.tgz", - "integrity": "sha512-1UzDldn9GfYYEsWWnn/P4wkTlkZDH7lDb0wBMGbtIQc9zXEQq7FlKBdZUn6OBqD8sKZZ2RQO2mAjGpXiDGoRmQ==" + "version": "2.2.11", + "resolved": "https://registry.npmjs.org/@types/classnames/-/classnames-2.2.11.tgz", + "integrity": "sha512-2koNhpWm3DgWRp5tpkiJ8JGc1xTn2q0l+jUNUE7oMKXUf5NpI9AIdC4kbjGNFBdHtcxBD18LAksoudAVhFKCjw==" }, "@types/color-name": { "version": "1.1.1", @@ -3768,9 +3594,9 @@ } }, "@types/history": { - "version": "4.7.5", - "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.5.tgz", - "integrity": "sha512-wLD/Aq2VggCJXSjxEwrMafIP51Z+13H78nXIX0ABEuIGhmB5sNGbR113MOKo+yfw+RDo1ZU3DM6yfnnRF/+ouw==" + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.8.tgz", + "integrity": "sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA==" }, "@types/hoist-non-react-statics": { "version": "3.3.1", @@ -3858,27 +3684,27 @@ "dev": true }, "@types/react": { - "version": "16.9.46", - "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.46.tgz", - "integrity": "sha512-dbHzO3aAq1lB3jRQuNpuZ/mnu+CdD3H0WVaaBQA8LTT3S33xhVBUj232T8M3tAhSWJs/D/UqORYUlJNl/8VQZg==", + "version": "16.9.56", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.56.tgz", + "integrity": "sha512-gIkl4J44G/qxbuC6r2Xh+D3CGZpJ+NdWTItAPmZbR5mUS+JQ8Zvzpl0ea5qT/ZT3ZNTUcDKUVqV3xBE8wv/DyQ==", "requires": { "@types/prop-types": "*", "csstype": "^3.0.2" } }, "@types/react-dom": { - "version": "16.9.8", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.8.tgz", - "integrity": "sha512-ykkPQ+5nFknnlU6lDd947WbQ6TE3NNzbQAkInC2EKY1qeYdTKp7onFusmYZb+ityzx2YviqT6BXSu+LyWWJwcA==", + "version": "16.9.9", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.9.tgz", + "integrity": "sha512-jE16FNWO3Logq/Lf+yvEAjKzhpST/Eac8EMd1i4dgZdMczfgqC8EjpxwNgEe3SExHYLliabXDh9DEhhqnlXJhg==", "dev": true, "requires": { "@types/react": "*" } }, "@types/react-redux": { - "version": "7.1.9", - "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.9.tgz", - "integrity": "sha512-mpC0jqxhP4mhmOl3P4ipRsgTgbNofMRXJb08Ms6gekViLj61v1hOZEKWDCyWsdONr6EjEA6ZHXC446wdywDe0w==", + "version": "7.1.11", + "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.11.tgz", + "integrity": "sha512-OjaFlmqy0CRbYKBoaWF84dub3impqnLJUrz4u8PRjDzaa4n1A2cVmjMV81shwXyAD5x767efhA8STFGJz/r1Zg==", "requires": { "@types/hoist-non-react-statics": "^3.3.0", "@types/react": "*", @@ -3887,18 +3713,18 @@ } }, "@types/react-router": { - "version": "5.1.7", - "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.7.tgz", - "integrity": "sha512-2ouP76VQafKjtuc0ShpwUebhHwJo0G6rhahW9Pb8au3tQTjYXd2jta4wv6U2tGLR/I42yuG00+UXjNYY0dTzbg==", + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.8.tgz", + "integrity": "sha512-HzOyJb+wFmyEhyfp4D4NYrumi+LQgQL/68HvJO+q6XtuHSDvw6Aqov7sCAhjbNq3bUPgPqbdvjXC5HeB2oEAPg==", "requires": { "@types/history": "*", "@types/react": "*" } }, "@types/react-router-dom": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.1.5.tgz", - "integrity": "sha512-ArBM4B1g3BWLGbaGvwBGO75GNFbLDUthrDojV2vHLih/Tq8M+tgvY1DSwkuNrPSwdp/GUL93WSEpTZs8nVyJLw==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.1.6.tgz", + "integrity": "sha512-gjrxYqxz37zWEdMVvQtWPFMFj1dRDb4TGOcgyOfSXTrEXdF92L00WE3C471O3TV/RF1oskcStkXsOU0Ete4s/g==", "requires": { "@types/history": "*", "@types/react": "*", @@ -4554,26 +4380,45 @@ } }, "babel-loader": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.1.0.tgz", - "integrity": "sha512-7q7nC1tYOrqvUrN3LQK4GwSk/TQorZSOlO9C+RZDZpODgyN4ZlCqE5q9cDsyWOliN+aU9B4JX01xK9eJXowJLw==", + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.1.tgz", + "integrity": "sha512-dMF8sb2KQ8kJl21GUjkW1HWmcsL39GOV5vnzjqrCzEPNY0S0UfMLnumidiwIajDSBmKhYf5iRW+HXaM4cvCKBw==", "dev": true, "requires": { "find-cache-dir": "^2.1.0", "loader-utils": "^1.4.0", - "mkdirp": "^0.5.3", + "make-dir": "^2.1.0", "pify": "^4.0.1", "schema-utils": "^2.6.5" }, "dependencies": { - "schema-utils": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.6.tgz", - "integrity": "sha512-wHutF/WPSbIi9x6ctjGGk2Hvl0VOz5l3EKEuKbjPlB30mKZUzb9A5k9yEXRX3pwyqVLPvpfZZEllaFq/M718hA==", + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "requires": { - "ajv": "^6.12.0", - "ajv-keywords": "^3.4.1" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true + }, + "schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" } } } @@ -4695,9 +4540,9 @@ } }, "base64-js": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", - "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, "batch": { "version": "0.6.1", @@ -4918,19 +4763,12 @@ } }, "browserify-rsa": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", - "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", + "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", "requires": { - "bn.js": "^4.1.0", + "bn.js": "^5.0.0", "randombytes": "^2.0.1" - }, - "dependencies": { - "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" - } } }, "browserify-sign": { @@ -5507,20 +5345,51 @@ "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" }, "core-js": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", - "integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==" + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.7.0.tgz", + "integrity": "sha512-NwS7fI5M5B85EwpWuIwJN4i/fbisQUwLwiSNUWeXlkAZ0sbBjLEvLvFLf1uzAUV66PcEPt4xCGCmOZSxVf3xzA==" }, "core-js-compat": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.5.tgz", - "integrity": "sha512-7ItTKOhOZbznhXAQ2g/slGg1PJV5zDO/WdkTwi7UEOJmkvsE32PWvx6mKtDjiMpjnR2CNf6BAD6sSxIlv7ptng==", + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.7.0.tgz", + "integrity": "sha512-V8yBI3+ZLDVomoWICO6kq/CD28Y4r1M7CWeO4AGpMdMfseu8bkSubBmUPySMGKRTS+su4XQ07zUkAsiu9FCWTg==", "dev": true, "requires": { - "browserslist": "^4.8.5", + "browserslist": "^4.14.6", "semver": "7.0.0" }, "dependencies": { + "browserslist": { + "version": "4.14.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.7.tgz", + "integrity": "sha512-BSVRLCeG3Xt/j/1cCGj1019Wbty0H+Yvu2AOuZSuoaUWn3RatbL33Cxk+Q4jRMRAbOm0p7SLravLjpnT6s0vzQ==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001157", + "colorette": "^1.2.1", + "electron-to-chromium": "^1.3.591", + "escalade": "^3.1.1", + "node-releases": "^1.1.66" + } + }, + "caniuse-lite": { + "version": "1.0.30001158", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001158.tgz", + "integrity": "sha512-s5loVYY+yKpuVA3HyW8BarzrtJvwHReuzugQXlv1iR3LKSReoFXRm86mT6hT7PEF5RxW+XQZg+6nYjlywYzQ+g==", + "dev": true + }, + "electron-to-chromium": { + "version": "1.3.596", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.596.tgz", + "integrity": "sha512-nLO2Wd2yU42eSoNJVQKNf89CcEGqeFZd++QsnN2XIgje1s/19AgctfjLIbPORlvcCO8sYjLwX4iUgDdusOY8Sg==", + "dev": true + }, + "node-releases": { + "version": "1.1.66", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.66.tgz", + "integrity": "sha512-JHEQ1iWPGK+38VLB2H9ef2otU4l8s3yAMt9Xf934r6+ojCYDMHPMqvCc9TnzfeFSP1QEOeU6YZEd3+De0LTCgg==", + "dev": true + }, "semver": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", @@ -5899,9 +5768,9 @@ } }, "csstype": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.2.tgz", - "integrity": "sha512-ofovWglpqoqbfLNOTBNZLSbMuGrblAf1efvvArGKOZMBrIoJeu5UsAipQolkijtyQx5MtAzT/J9IHj/CEY1mJw==" + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.5.tgz", + "integrity": "sha512-uVDi8LpBUKQj6sdxNaTetL6FpeCqTjOvAQuQUa/qAqq8oOd4ivkbhgnqayl0dnPal8Tb/yB1tF+gOvCBiicaiQ==" }, "currently-unhandled": { "version": "0.4.1", @@ -6374,6 +6243,12 @@ "is-symbol": "^1.0.2" } }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -6423,11 +6298,18 @@ "dev": true }, "esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "requires": { - "estraverse": "^4.1.0" + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==" + } } }, "estraverse": { @@ -6800,15 +6682,39 @@ "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==" }, "file-loader": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.0.0.tgz", - "integrity": "sha512-/aMOAYEFXDdjG0wytpTL5YQLfZnnTmLNjn+AIrJ/6HVnTfDqLsVKUUwkDf4I4kgex36BvjuXEn/TX9B/1ESyqQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", "dev": true, "requires": { "loader-utils": "^2.0.0", - "schema-utils": "^2.6.5" + "schema-utils": "^3.0.0" }, "dependencies": { + "@types/json-schema": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz", + "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==", + "dev": true + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true + }, "json5": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", @@ -6830,13 +6736,14 @@ } }, "schema-utils": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.6.tgz", - "integrity": "sha512-wHutF/WPSbIi9x6ctjGGk2Hvl0VOz5l3EKEuKbjPlB30mKZUzb9A5k9yEXRX3pwyqVLPvpfZZEllaFq/M718hA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", "dev": true, "requires": { - "ajv": "^6.12.0", - "ajv-keywords": "^3.4.1" + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" } } } @@ -7602,9 +7509,9 @@ } }, "gensync": { - "version": "1.0.0-beta.1", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", - "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true }, "get-caller-file": { @@ -7930,9 +7837,9 @@ "dev": true }, "highlight.js": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.1.2.tgz", - "integrity": "sha512-Q39v/Mn5mfBlMff9r+zzA+gWxRsCRKwEMvYTiisLr/XUiFI/4puWt0Ojdko3R3JCNWGdOWaA5g/Yxqa23kC5AA==" + "version": "10.3.2", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.3.2.tgz", + "integrity": "sha512-3jRT7OUYsVsKvukNKZCtnvRcFyCJqSEIuIMsEybAXRiFSwpt65qjPd/Pr+UOdYt7WJlt+lj3+ypUsHiySBp/Jw==" }, "history": { "version": "4.10.1", @@ -8122,9 +8029,9 @@ } }, "ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" }, "iferr": { "version": "0.1.5", @@ -8281,15 +8188,6 @@ "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", "dev": true }, - "invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dev": true, - "requires": { - "loose-envify": "^1.0.0" - } - }, "ip": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", @@ -10364,15 +10262,6 @@ "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", "dev": true }, - "levenary": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/levenary/-/levenary-1.1.1.tgz", - "integrity": "sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ==", - "dev": true, - "requires": { - "leven": "^3.1.0" - } - }, "levn": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", @@ -10463,8 +10352,7 @@ "lodash": { "version": "4.17.20", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", - "dev": true + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" }, "lodash.memoize": { "version": "4.1.2", @@ -10948,9 +10836,9 @@ "dev": true }, "node-forge": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.0.tgz", - "integrity": "sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ==", + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", + "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", "dev": true }, "node-gyp": { @@ -12434,9 +12322,9 @@ "dev": true }, "prettier": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.5.tgz", - "integrity": "sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.1.2.tgz", + "integrity": "sha512-16c7K+x4qVlJg9rEbXl7HEGmQyZlG4R9AgP+oHKRMsMsuk8s+ATStlf1NpDqyBI1HpVyfjLOeMhH2LvuNvV5Vg==", "dev": true }, "pretty-format": { @@ -12676,9 +12564,9 @@ } }, "react": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react/-/react-16.13.1.tgz", - "integrity": "sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==", + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", + "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==", "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", @@ -12686,9 +12574,9 @@ } }, "react-dom": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.13.1.tgz", - "integrity": "sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag==", + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz", + "integrity": "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==", "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", @@ -12713,9 +12601,9 @@ } }, "react-hot-loader": { - "version": "4.12.21", - "resolved": "https://registry.npmjs.org/react-hot-loader/-/react-hot-loader-4.12.21.tgz", - "integrity": "sha512-Ynxa6ROfWUeKWsTHxsrL2KMzujxJVPjs385lmB2t5cHUxdoRPGind9F00tOkdc1l5WBleOF4XEAMILY1KPIIDA==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/react-hot-loader/-/react-hot-loader-4.13.0.tgz", + "integrity": "sha512-JrLlvUPqh6wIkrK2hZDfOyq/Uh/WeVEr8nc7hkn2/3Ul0sx1Kr5y4kOGNacNRoj7RhwLNcQ3Udf1KJXrqc0ZtA==", "requires": { "fast-levenshtein": "^2.0.6", "global": "^4.3.0", @@ -12745,15 +12633,25 @@ "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" }, "react-redux": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.1.tgz", - "integrity": "sha512-T+VfD/bvgGTUA74iW9d2i5THrDQWbweXP0AVNI8tNd1Rk5ch1rnMiJkDD67ejw7YBKM4+REvcvqRuWJb7BLuEg==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.2.tgz", + "integrity": "sha512-8+CQ1EvIVFkYL/vu6Olo7JFLWop1qRUeb46sGtIMDCSpgwPQq8fPLpirIB0iTqFe9XYEFPHssdX8/UwN6pAkEA==", "requires": { - "@babel/runtime": "^7.5.5", - "hoist-non-react-statics": "^3.3.0", + "@babel/runtime": "^7.12.1", + "hoist-non-react-statics": "^3.3.2", "loose-envify": "^1.4.0", "prop-types": "^15.7.2", - "react-is": "^16.9.0" + "react-is": "^16.13.1" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.5.tgz", + "integrity": "sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + } } }, "react-router": { @@ -12944,9 +12842,9 @@ "integrity": "sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw==" }, "regenerate": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.1.tgz", - "integrity": "sha512-j2+C8+NtXQgEKWk49MMP5P/u2GhnahTtVkRIHr5R5lVRlbKvmQ+oS+A5aLKWp2ma5VkT8sh6v+v4hbH0YHR66A==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", "dev": true }, "regenerate-unicode-properties": { @@ -12992,9 +12890,9 @@ } }, "regexpu-core": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.0.tgz", - "integrity": "sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ==", + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.1.tgz", + "integrity": "sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ==", "dev": true, "requires": { "regenerate": "^1.4.0", @@ -13497,12 +13395,12 @@ "dev": true }, "selfsigned": { - "version": "1.10.7", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.7.tgz", - "integrity": "sha512-8M3wBCzeWIJnQfl43IKwOmC4H/RAp50S8DF60znzjW5GVqTcSe2vWclt7hmYVPkKPlHWOu5EaWOMZ2Y6W8ZXTA==", + "version": "1.10.8", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.8.tgz", + "integrity": "sha512-2P4PtieJeEwVgTU9QEcwIRDQ/mXJLX8/+I3ur+Pg16nS8oNbrGxEso9NyYWy8NAmXiNl4dlAp5MwoNeCWzON4w==", "dev": true, "requires": { - "node-forge": "0.9.0" + "node-forge": "^0.10.0" } }, "semver": { @@ -14301,15 +14199,33 @@ } }, "style-loader": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-1.2.1.tgz", - "integrity": "sha512-ByHSTQvHLkWE9Ir5+lGbVOXhxX10fbprhLvdg96wedFZb4NDekDPxVKv5Fwmio+QcMlkkNfuK+5W1peQ5CUhZg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-1.3.0.tgz", + "integrity": "sha512-V7TCORko8rs9rIqkSrlMfkqA63DfoGBBJmK1kKGCcSi+BWb4cqz0SRsnp4l6rU5iwOEd0/2ePv68SV22VXon4Q==", "dev": true, "requires": { "loader-utils": "^2.0.0", - "schema-utils": "^2.6.6" + "schema-utils": "^2.7.0" }, "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true + }, "json5": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", @@ -14331,14 +14247,14 @@ } }, "schema-utils": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", - "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", "dev": true, "requires": { - "@types/json-schema": "^7.0.4", - "ajv": "^6.12.2", - "ajv-keywords": "^3.4.1" + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" } } } @@ -14533,9 +14449,9 @@ "dev": true }, "timers-browserify": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.11.tgz", - "integrity": "sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ==", + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", + "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", "requires": { "setimmediate": "^1.0.4" } @@ -14794,9 +14710,9 @@ } }, "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "tty-browserify": { "version": "0.0.0", @@ -15035,16 +14951,40 @@ } }, "url-loader": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.0.tgz", - "integrity": "sha512-IzgAAIC8wRrg6NYkFIJY09vtktQcsvU8V6HhtQj9PTefbYImzLB1hufqo4m+RyM5N3mLx5BqJKccgxJS+W3kqw==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz", + "integrity": "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==", "dev": true, "requires": { "loader-utils": "^2.0.0", - "mime-types": "^2.1.26", - "schema-utils": "^2.6.5" + "mime-types": "^2.1.27", + "schema-utils": "^3.0.0" }, "dependencies": { + "@types/json-schema": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz", + "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==", + "dev": true + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true + }, "json5": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", @@ -15066,13 +15006,14 @@ } }, "schema-utils": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.6.tgz", - "integrity": "sha512-wHutF/WPSbIi9x6ctjGGk2Hvl0VOz5l3EKEuKbjPlB30mKZUzb9A5k9yEXRX3pwyqVLPvpfZZEllaFq/M718hA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", "dev": true, "requires": { - "ajv": "^6.12.0", - "ajv-keywords": "^3.4.1" + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" } } } @@ -15234,14 +15175,14 @@ } }, "watchpack": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.4.tgz", - "integrity": "sha512-aWAgTW4MoSJzZPAicljkO1hsi1oKj/RRq/OJQh2PKI2UKL04c2Bs+MBOB+BBABHTXJpf9mCwHN7ANCvYsvY2sg==", + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz", + "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==", "requires": { "chokidar": "^3.4.1", "graceful-fs": "^4.1.2", "neo-async": "^2.5.0", - "watchpack-chokidar2": "^2.0.0" + "watchpack-chokidar2": "^2.0.1" }, "dependencies": { "anymatch": { @@ -15270,9 +15211,9 @@ } }, "chokidar": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.2.tgz", - "integrity": "sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz", + "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==", "optional": true, "requires": { "anymatch": "~3.1.1", @@ -15282,7 +15223,7 @@ "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.4.0" + "readdirp": "~3.5.0" } }, "fill-range": { @@ -15325,9 +15266,9 @@ "optional": true }, "readdirp": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", - "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", "optional": true, "requires": { "picomatch": "^2.2.1" @@ -15345,9 +15286,9 @@ } }, "watchpack-chokidar2": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.0.tgz", - "integrity": "sha512-9TyfOyN/zLUbA288wZ8IsMZ+6cbzvsNyEzSBp6e/zkifi6xxbl8SmQ/CxQq32k8NNqrdVEVUVSEf56L4rQ/ZxA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz", + "integrity": "sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==", "optional": true, "requires": { "chokidar": "^2.1.8" @@ -15369,9 +15310,9 @@ "dev": true }, "webpack": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.44.1.tgz", - "integrity": "sha512-4UOGAohv/VGUNQJstzEywwNxqX417FnjZgZJpJQegddzPmTvph37eBIRbRTfdySXzVtJXLJfbMN3mMYhM6GdmQ==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.44.2.tgz", + "integrity": "sha512-6KJVGlCxYdISyurpQ0IPTklv+DULv05rs2hseIXer6D7KrUicRDLFb4IUM1S6LUAKypPM/nSiVSuv8jHu1m3/Q==", "requires": { "@webassemblyjs/ast": "1.9.0", "@webassemblyjs/helper-module-context": "1.9.0", diff --git a/web/package.json b/web/package.json index 05aa4a40..075f56ca 100644 --- a/web/package.json +++ b/web/package.json @@ -13,34 +13,34 @@ "author": "Monomax Software Pty Ltd", "license": "AGPL-3.0-or-later", "devDependencies": { - "@babel/core": "^7.11.4", - "@babel/plugin-transform-react-constant-elements": "^7.10.4", - "@babel/preset-env": "^7.11.0", - "@babel/preset-react": "^7.10.4", - "@babel/preset-typescript": "^7.10.4", - "@babel/register": "^7.10.5", - "@hot-loader/react-dom": "^16.13.0", + "@babel/core": "^7.12.3", + "@babel/plugin-transform-react-constant-elements": "^7.12.1", + "@babel/preset-env": "^7.12.1", + "@babel/preset-react": "^7.12.5", + "@babel/preset-typescript": "^7.12.1", + "@babel/register": "^7.12.1", + "@hot-loader/react-dom": "^16.14.0", "@types/jest": "^25.2.3", - "@types/react": "^16.9.46", - "@types/react-dom": "^16.9.8", + "@types/react": "^16.9.56", + "@types/react-dom": "^16.9.9", "autoprefixer": "^9.8.6", - "babel-loader": "^8.1.0", + "babel-loader": "^8.2.1", "css-loader": "^3.6.0", "cssnano": "^4.1.10", - "file-loader": "^6.0.0", + "file-loader": "^6.2.0", "jest": "^25.5.4", "mini-css-extract-plugin": "^0.9.0", "node-sass": "^4.14.1", "postcss-loader": "^3.0.0", - "prettier": "^2.0.0", + "prettier": "^2.1.2", "sass-loader": "^8.0.2", "source-map-loader": "^0.2.4", "source-map-support": "^0.5.19", - "style-loader": "^1.2.1", + "style-loader": "^1.3.0", "ts-jest": "^25.5.1", "ts-loader": "^7.0.5", "typescript": "^3.9.7", - "url-loader": "^4.1.0", + "url-loader": "^4.1.1", "webpack-cli": "^3.3.12", "webpack-dev-middleware": "^3.7.2", "webpack-dev-server": "^3.11.0", @@ -48,27 +48,27 @@ "webpack-manifest-plugin": "^2.2.0" }, "dependencies": { - "@babel/plugin-proposal-class-properties": "^7.10.4", - "@types/classnames": "^2.2.10", - "@types/react-redux": "^7.1.9", - "@types/react-router-dom": "^5.1.5", + "@babel/plugin-proposal-class-properties": "^7.12.1", + "@types/classnames": "^2.2.11", + "@types/react-redux": "^7.1.11", + "@types/react-router-dom": "^5.1.6", "classnames": "^2.2.5", - "core-js": "^3.6.5", - "highlight.js": "^10.1.2", + "core-js": "^3.7.0", + "highlight.js": "^10.3.2", "history": "^4.10.1", "markdown-it": "^9.0.0", "qs": "^6.9.4", - "react": "^16.13.1", - "react-dom": "^16.13.1", + "react": "^16.14.0", + "react-dom": "^16.14.0", "react-helmet": "^5.0.0", - "react-hot-loader": "^4.12.21", - "react-redux": "^7.2.1", + "react-hot-loader": "^4.13.0", + "react-redux": "^7.2.2", "react-router": "^5.2.0", "react-router-config": "^5.1.1", "react-router-dom": "^5.2.0", "redux": "^4.0.5", "redux-thunk": "^2.1.0", "regenerator-runtime": "^0.13.7", - "webpack": "^4.44.1" + "webpack": "^4.44.2" } } From 1b7473149c76ba69d8f1587ed747ded82d757d9e Mon Sep 17 00:00:00 2001 From: Sung Won Cho Date: Sun, 3 Jan 2021 11:37:34 +1100 Subject: [PATCH 02/91] Update install_postgres.sh (#525) --- scripts/vagrant/install_postgres.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/vagrant/install_postgres.sh b/scripts/vagrant/install_postgres.sh index bb8d8cfc..bdacd51f 100755 --- a/scripts/vagrant/install_postgres.sh +++ b/scripts/vagrant/install_postgres.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -ex -sudo apt-get install wget ca-certificates +sudo apt-get -y install wget ca-certificates wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add - sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt/ `lsb_release -cs`-pgdg main" >> /etc/apt/sources.list.d/pgdg.list' From e9f3b080d5d75a6f2ace2b6ba7a18d7c039f401d Mon Sep 17 00:00:00 2001 From: Sung Won Cho Date: Sun, 3 Jan 2021 12:11:22 +1100 Subject: [PATCH 03/91] Use XDG base directory (#527) * Create platform specific directory definitions * Fix CLI integration test * Rename dirs to paths and get config path * Namespace * Fix initialization of dirs * Simplify and change description * Simplify * Fix build flag * Bump sqlite version * Bump xgo --- go.mod | 30 ++--- go.sum | 197 ++++++++++++++++++------------ pkg/cli/cmd/root/root.go | 2 +- pkg/cli/cmd/sync/main_test.go | 17 +++ pkg/cli/cmd/sync/sync_test.go | 22 ++-- pkg/cli/config/config.go | 23 +++- pkg/cli/consts/consts.go | 4 +- pkg/cli/context/ctx.go | 12 +- pkg/cli/context/testutils.go | 21 ++-- pkg/cli/database/testutils.go | 2 +- pkg/cli/dirs/dirs.go | 49 ++++++++ pkg/cli/dirs/dirs_test.go | 25 ++++ pkg/cli/dirs/dirs_unix.go | 33 +++++ pkg/cli/dirs/dirs_unix_test.go | 66 ++++++++++ pkg/cli/dirs/dirs_windows.go | 14 +++ pkg/cli/dirs/dirs_windows_test.go | 41 +++++++ pkg/cli/infra/init.go | 107 +++++++++------- pkg/cli/main_test.go | 49 ++++---- pkg/cli/migrate/legacy.go | 36 +++--- pkg/cli/migrate/legacy_test.go | 28 +++-- pkg/cli/migrate/migrate_test.go | 48 +++++--- pkg/cli/testutils/main.go | 13 +- pkg/cli/ui/editor.go | 2 +- pkg/cli/ui/editor_test.go | 27 ++-- 24 files changed, 619 insertions(+), 249 deletions(-) create mode 100644 pkg/cli/cmd/sync/main_test.go create mode 100644 pkg/cli/dirs/dirs.go create mode 100644 pkg/cli/dirs/dirs_test.go create mode 100644 pkg/cli/dirs/dirs_unix.go create mode 100644 pkg/cli/dirs/dirs_unix_test.go create mode 100644 pkg/cli/dirs/dirs_windows.go create mode 100644 pkg/cli/dirs/dirs_windows_test.go diff --git a/go.mod b/go.mod index 6f23ece3..14d37869 100644 --- a/go.mod +++ b/go.mod @@ -3,37 +3,39 @@ module github.com/dnote/dnote go 1.13 require ( + github.com/PuerkitoBio/goquery v1.6.0 // indirect github.com/andybalholm/cascadia v1.2.0 // indirect github.com/aymerick/douceur v0.2.0 github.com/dnote/actions v0.2.0 github.com/dnote/color v1.7.0 - github.com/gobuffalo/packr/v2 v2.8.0 - github.com/google/go-cmp v0.5.1 + github.com/dnote/xgo v0.0.0-20200205013105-40be7d6d43ff // indirect + github.com/gobuffalo/packr/v2 v2.8.1 + github.com/google/go-cmp v0.5.4 github.com/google/go-github v17.0.0+incompatible github.com/google/go-querystring v1.0.0 // indirect - github.com/google/uuid v1.1.1 + github.com/google/uuid v1.1.3 github.com/gorilla/css v1.0.0 // indirect github.com/gorilla/mux v1.8.0 github.com/jinzhu/gorm v1.9.16 github.com/joho/godotenv v1.3.0 github.com/karrick/godirwalk v1.16.1 // indirect - github.com/lib/pq v1.8.0 - github.com/mattn/go-colorable v0.1.7 // indirect - github.com/mattn/go-sqlite3 v2.0.3+incompatible + github.com/lib/pq v1.9.0 + github.com/mattn/go-colorable v0.1.8 // indirect + github.com/mattn/go-sqlite3 v1.14.6 github.com/pkg/errors v0.9.1 github.com/radovskyb/watcher v1.0.7 github.com/robfig/cron v1.2.0 github.com/rubenv/sql-migrate v0.0.0-20200616145509-8d140a17f351 github.com/sergi/go-diff v1.1.0 - github.com/sirupsen/logrus v1.6.0 // indirect - github.com/spf13/cobra v1.0.0 - github.com/spf13/pflag v1.0.5 // indirect - golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a - golang.org/x/net v0.0.0-20200822124328-c89045814202 // indirect + github.com/sirupsen/logrus v1.7.0 // indirect + github.com/spf13/cobra v1.1.1 + golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad + golang.org/x/net v0.0.0-20201224014010-6772e930b67b // indirect golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a // indirect - golang.org/x/sys v0.0.0-20200821140526-fda516888d29 // indirect - golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e + golang.org/x/sys v0.0.0-20201231184435-2d18734c6014 // indirect + golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect + golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df - gopkg.in/yaml.v2 v2.3.0 + gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index e3341365..238bdc3a 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,24 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/goquery v1.5.1 h1:PSPBGne8NIUWw+/7vFBV+kG2J/5MOjbzc7154OaKCSE= github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= +github.com/PuerkitoBio/goquery v1.6.0 h1:j7taAbelrdcsOlGeMenZxc2AWXD5fieT1/znArdnx94= +github.com/PuerkitoBio/goquery v1.6.0/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= @@ -13,7 +27,6 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/andybalholm/cascadia v1.1.0 h1:BuuO6sSfQNFRu1LppgbD25Hr2vLYW25JvxHs5zzsLTo= github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= github.com/andybalholm/cascadia v1.2.0 h1:vuRCkM5Ozh/BfmsaTm26kbjm0mIOM3yS5Ek/F5h18aE= github.com/andybalholm/cascadia v1.2.0/go.mod h1:YCyR8vOZT9aZ1CHEd8ap0gMVm2aFgxBp0T0eFw1RUQY= @@ -33,6 +46,7 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -44,8 +58,10 @@ github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:z github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= @@ -66,6 +82,8 @@ github.com/dnote/actions v0.2.0 h1:P1ut2/QRKwfAzIIB374vN9A4IanU94C/payEocvngYo= github.com/dnote/actions v0.2.0/go.mod h1:bBIassLhppVQdbC3iaE92SHBpM1HOVe+xZoAlj9ROxw= github.com/dnote/color v1.7.0 h1:8/QGLQKSU8/zcWQaHbMyC1hJRkKO/Uu9M89sH76ecHE= github.com/dnote/color v1.7.0/go.mod h1:75UcP/TH7CNvjQ5pwDumkUS3vkPdGggy7/3fT8MlxHM= +github.com/dnote/xgo v0.0.0-20200205013105-40be7d6d43ff h1:DJKdzouhr6u1NzuLbmSWeei9BagH3Nm4mSOzP0RMdc0= +github.com/dnote/xgo v0.0.0-20200205013105-40be7d6d43ff/go.mod h1:ruGZjl8WThApI7BAIKV2Q/PnJoudvd6Epjc3z79jWVg= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= @@ -81,6 +99,7 @@ github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVB github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= @@ -88,25 +107,21 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= -github.com/gobuffalo/envy v1.7.1 h1:OQl5ys5MBea7OGCdvPbBJWRgnhC/fGona6QKfvFeau8= github.com/gobuffalo/envy v1.7.1/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w= -github.com/gobuffalo/logger v1.0.1 h1:ZEgyRGgAm4ZAhAO45YXMs5Fp+bzGLESFewzAVBMKuTg= github.com/gobuffalo/logger v1.0.1/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs= github.com/gobuffalo/logger v1.0.3 h1:YaXOTHNPCvkqqA7w05A4v0k2tCdpr+sgFlgINbQ6gqc= github.com/gobuffalo/logger v1.0.3/go.mod h1:SoeejUwldiS7ZsyCBphOGURmWdwUFXs0J7TCjEhjKxM= -github.com/gobuffalo/packd v0.3.0 h1:eMwymTkA1uXsqxS0Tpoop3Lc0u3kTfiMBE6nKtQU4g4= github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q= github.com/gobuffalo/packd v1.0.0 h1:6ERZvJHfe24rfFmA9OaoKBdC7+c9sydrytMg8SdFGBM= github.com/gobuffalo/packd v1.0.0/go.mod h1:6VTc4htmJRFB7u1m/4LeMTWjFoYrUiBkU9Fdec9hrhI= -github.com/gobuffalo/packr/v2 v2.7.1 h1:n3CIW5T17T8v4GGK5sWXLVWJhCz7b5aNLSxW6gYim4o= github.com/gobuffalo/packr/v2 v2.7.1/go.mod h1:qYEvAazPaVxy7Y7KR0W8qYEE+RymX74kETFqjFoFlOc= -github.com/gobuffalo/packr/v2 v2.8.0 h1:IULGd15bQL59ijXLxEvA5wlMxsmx/ZkQv9T282zNVIY= -github.com/gobuffalo/packr/v2 v2.8.0/go.mod h1:PDk2k3vGevNE3SwVyVRgQCCXETC9SaONCNSXT1Q8M1g= +github.com/gobuffalo/packr/v2 v2.8.1 h1:tkQpju6i3EtMXJ9uoF5GT6kB+LMTimDWD8Xvbz6zDVA= +github.com/gobuffalo/packr/v2 v2.8.1/go.mod h1:c/PLlOuTU+p3SybaJATW3H6lX/iK7xEz5OeMf+NnJpg= github.com/godror/godror v0.13.3/go.mod h1:2ouUT4kdhUBk7TAkHWD4SN0CdI0pgEQbo8FVHhbSKWg= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -119,6 +134,8 @@ github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4er github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -127,39 +144,43 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.3 h1:twObb+9XcuH5B9V1TBCvvvZoO6iEdILi2a76PYn5rJI= +github.com/google/uuid v1.1.3/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc= -github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= @@ -185,8 +206,6 @@ github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmK github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= -github.com/jinzhu/gorm v1.9.12 h1:Drgk1clyWT9t9ERbzHza6Mj/8FY/CqMyVzOiHviMo6Q= -github.com/jinzhu/gorm v1.9.12/go.mod h1:vhTjlKSJUTWNtcbQtrMBFCxy7eXTzeCAzfL5fBZT/Qs= github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o= github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= @@ -200,50 +219,40 @@ github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22 github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/karrick/godirwalk v1.15.3 h1:0a2pXOgtB16CqIqXTiT7+K9L73f74n/aNQUnH6Ortew= -github.com/karrick/godirwalk v1.15.3/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= -github.com/karrick/godirwalk v1.15.6 h1:Yf2mmR8TJy+8Fa0SuQVto5SYap6IF7lNVX4Jdl8G1qA= -github.com/karrick/godirwalk v1.15.6/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= +github.com/karrick/godirwalk v1.15.8/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw= github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4= github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.5.2 h1:yTSXVswvWUOQ3k1sd7vJfDrbSl8lKuscqFJRqjC0ifw= -github.com/lib/pq v1.5.2/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.8.0 h1:9xohqzkUwzR4Ga4ivdTcawVS89YSDVxXMa3xJX3cGzg= -github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.9.0 h1:L8nSXQQzAYByakOFMTwpjRoHsMJklur4Gi59b6VivR8= +github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/markbates/errx v1.1.0 h1:QDFeR+UP95dO12JgW+tgi2UVfo0V8YBHiUIOaeBPiEI= github.com/markbates/errx v1.1.0/go.mod h1:PLa46Oex9KNbVDZhKel8v1OT7hD5JZ2eI7AHhA0wswc= github.com/markbates/oncer v1.0.0 h1:E83IaVAHygyndzPimgUYJjbshhDTALZyXxvk9FOlQRY= github.com/markbates/oncer v1.0.0/go.mod h1:Z59JA581E9GP6w96jai+TGqafHPW+cPfRxz2aSZ0mcI= github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI= github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= -github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE= -github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw= -github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= @@ -253,7 +262,8 @@ github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-sqlite3 v1.12.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus= -github.com/mattn/go-sqlite3 v2.0.1+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg= +github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U= github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -304,7 +314,6 @@ github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9 github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -342,12 +351,9 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.4.0 h1:LUa41nrWTQNGhzdsZ5lTnkwbNjj6rXTdazA1cSdjkOY= github.com/rogpeppe/go-internal v1.4.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.5.2 h1:qLvObTrvO/XRCqmkKxUlOBc48bI3efyDuAZe25QiF0w= github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rubenv/sql-migrate v0.0.0-20200429072036-ae26b214fa43 h1:0i6uTtxUGc/jpK/CngM4T2S2NFnqYUUxH+lKDgBLw8U= -github.com/rubenv/sql-migrate v0.0.0-20200429072036-ae26b214fa43/go.mod h1:DCgfY80j8GYL7MLEfvcpSFvjD0L5yZq/aZUJmhZklyg= github.com/rubenv/sql-migrate v0.0.0-20200616145509-8d140a17f351 h1:HXr/qUllAWv9riaI4zh2eXWKmCSDqVS/XH1MRHLKRwk= github.com/rubenv/sql-migrate v0.0.0-20200616145509-8d140a17f351/go.mod h1:DCgfY80j8GYL7MLEfvcpSFvjD0L5yZq/aZUJmhZklyg= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= @@ -359,10 +365,9 @@ github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= @@ -371,19 +376,18 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= -github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4= +github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= @@ -391,10 +395,10 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= @@ -410,6 +414,8 @@ go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -425,24 +431,32 @@ golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4 h1:ydJNl0ENAG67pFbB+9tfhiL2pYqLhfoaZFw/cjLhY4A= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 h1:cg5LA/zNPRzIXIWSCxQW10Rvpy94aQh3LT/ShoCpkHw= -golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM= -golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -458,27 +472,26 @@ golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120 h1:EZ3cVSzKOlJxAd8e8YAJ7no8nNypTxexh/YE/xW3ZEY= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b h1:iFwSg7t5GZmB/Q5TjiEAsdoLDrdJRC1RiF2WhuV29Qw= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -492,69 +505,96 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200513112337-417ce2331b5c h1:kISX68E8gSkNYAFRFiDU8rl5RIn1sJYKYb/r2vMLDrU= -golang.org/x/sys v0.0.0-20200513112337-417ce2331b5c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200821140526-fda516888d29 h1:mNuhGagCf3lDDm5C0376C/sxh6V7fy9WbdEu/YDNA04= -golang.org/x/sys v0.0.0-20200821140526-fda516888d29/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201231184435-2d18734c6014 h1:joucsQqXmyBVxViHCPFjG3hx8JzIFSaym3l3MM/Jsdg= +golang.org/x/sys v0.0.0-20201231184435-2d18734c6014/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf h1:MZ2shdL+ZM/XzY3ZGOnh4Nlpnxz5GSOhOmtHo3iPU6M= +golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI= -golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s= -golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 h1:Hir2P/De0WpUhtrKGGjvSb2YxUgyZ7EFOSLIcSSpiwE= +golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191004055002-72853e10c5a3/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200308013534-11ec41452d41 h1:9Di9iYgOt9ThCipBxChBVhgNipDoE5mxO84rQV7D0FE= golang.org/x/tools v0.0.0-20200308013534-11ec41452d41/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= @@ -563,7 +603,6 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -575,20 +614,24 @@ gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AW gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= gopkg.in/gorp.v1 v1.7.2 h1:j3DWlAyGVv8whO7AcIWznQ2Yj7yJkn34B8s63GViAAw= gopkg.in/gorp.v1 v1.7.2/go.mod h1:Wo3h+DBQZIxATwftsglhdD/62zRFPhGhTiu5jUJmCaw= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/pkg/cli/cmd/root/root.go b/pkg/cli/cmd/root/root.go index 5076e709..5e3db92f 100644 --- a/pkg/cli/cmd/root/root.go +++ b/pkg/cli/cmd/root/root.go @@ -24,7 +24,7 @@ import ( var root = &cobra.Command{ Use: "dnote", - Short: "Dnote - Instantly capture what you learn while coding", + Short: "Dnote - a simple command line notebook", SilenceErrors: true, SilenceUsage: true, } diff --git a/pkg/cli/cmd/sync/main_test.go b/pkg/cli/cmd/sync/main_test.go new file mode 100644 index 00000000..c869afb7 --- /dev/null +++ b/pkg/cli/cmd/sync/main_test.go @@ -0,0 +1,17 @@ +package sync + +import ( + "github.com/dnote/dnote/pkg/cli/context" + "path/filepath" +) + +var testDir = "../../tmp" + +var paths context.Paths = context.Paths{ + Home: testDir, + Cache: testDir, + Config: testDir, + Data: testDir, +} + +var dbPath = filepath.Join(testDir, "test.db") diff --git a/pkg/cli/cmd/sync/sync_test.go b/pkg/cli/cmd/sync/sync_test.go index e5c6457a..f107ed15 100644 --- a/pkg/cli/cmd/sync/sync_test.go +++ b/pkg/cli/cmd/sync/sync_test.go @@ -36,8 +36,6 @@ import ( "github.com/pkg/errors" ) -var dbPath = "../../tmp/.dnote.db" - func TestProcessFragments(t *testing.T) { fragments := []client.SyncFragment{ { @@ -1822,7 +1820,7 @@ func TestMergeBook(t *testing.T) { func TestSaveServerState(t *testing.T) { // set up - ctx := context.InitTestCtx(t, "../../tmp", nil) + ctx := context.InitTestCtx(t, paths, nil) defer context.TeardownTestCtx(t, ctx) testutils.Login(t, &ctx) @@ -1866,7 +1864,7 @@ func TestSaveServerState(t *testing.T) { // are updated accordingly based on the server response. func TestSendBooks(t *testing.T) { // set up - ctx := context.InitTestCtx(t, "../../tmp", nil) + ctx := context.InitTestCtx(t, paths, nil) defer context.TeardownTestCtx(t, ctx) testutils.Login(t, &ctx) @@ -2099,7 +2097,7 @@ func TestSendBooks_isBehind(t *testing.T) { for idx, tc := range testCases { func() { // set up - ctx := context.InitTestCtx(t, "../../tmp", nil) + ctx := context.InitTestCtx(t, paths, nil) ctx.APIEndpoint = ts.URL defer context.TeardownTestCtx(t, ctx) testutils.Login(t, &ctx) @@ -2147,7 +2145,7 @@ func TestSendBooks_isBehind(t *testing.T) { for idx, tc := range testCases { func() { // set up - ctx := context.InitTestCtx(t, "../../tmp", nil) + ctx := context.InitTestCtx(t, paths, nil) ctx.APIEndpoint = ts.URL defer context.TeardownTestCtx(t, ctx) testutils.Login(t, &ctx) @@ -2195,7 +2193,7 @@ func TestSendBooks_isBehind(t *testing.T) { for idx, tc := range testCases { func() { // set up - ctx := context.InitTestCtx(t, "../../tmp", nil) + ctx := context.InitTestCtx(t, paths, nil) ctx.APIEndpoint = ts.URL defer context.TeardownTestCtx(t, ctx) testutils.Login(t, &ctx) @@ -2230,7 +2228,7 @@ func TestSendBooks_isBehind(t *testing.T) { // uuid from the incoming data. func TestSendNotes(t *testing.T) { // set up - ctx := context.InitTestCtx(t, "../../tmp", nil) + ctx := context.InitTestCtx(t, paths, nil) defer context.TeardownTestCtx(t, ctx) testutils.Login(t, &ctx) @@ -2383,7 +2381,7 @@ func TestSendNotes(t *testing.T) { func TestSendNotes_addedOn(t *testing.T) { // set up - ctx := context.InitTestCtx(t, "../../tmp", nil) + ctx := context.InitTestCtx(t, paths, nil) defer context.TeardownTestCtx(t, ctx) testutils.Login(t, &ctx) @@ -2515,7 +2513,7 @@ func TestSendNotes_isBehind(t *testing.T) { for idx, tc := range testCases { func() { // set up - ctx := context.InitTestCtx(t, "../../tmp", nil) + ctx := context.InitTestCtx(t, paths, nil) defer context.TeardownTestCtx(t, ctx) testutils.Login(t, &ctx) ctx.APIEndpoint = ts.URL @@ -2564,7 +2562,7 @@ func TestSendNotes_isBehind(t *testing.T) { for idx, tc := range testCases { func() { // set up - ctx := context.InitTestCtx(t, "../../tmp", nil) + ctx := context.InitTestCtx(t, paths, nil) defer context.TeardownTestCtx(t, ctx) testutils.Login(t, &ctx) ctx.APIEndpoint = ts.URL @@ -2613,7 +2611,7 @@ func TestSendNotes_isBehind(t *testing.T) { for idx, tc := range testCases { func() { // set up - ctx := context.InitTestCtx(t, "../../tmp", nil) + ctx := context.InitTestCtx(t, paths, nil) defer context.TeardownTestCtx(t, ctx) testutils.Login(t, &ctx) ctx.APIEndpoint = ts.URL diff --git a/pkg/cli/config/config.go b/pkg/cli/config/config.go index bb2e9424..8d0bd7dd 100644 --- a/pkg/cli/config/config.go +++ b/pkg/cli/config/config.go @@ -24,6 +24,8 @@ import ( "github.com/dnote/dnote/pkg/cli/consts" "github.com/dnote/dnote/pkg/cli/context" + "github.com/dnote/dnote/pkg/cli/log" + "github.com/dnote/dnote/pkg/cli/utils" "github.com/pkg/errors" "gopkg.in/yaml.v2" ) @@ -34,9 +36,28 @@ type Config struct { APIEndpoint string `yaml:"apiEndpoint"` } +func checkLegacyPath(ctx context.DnoteCtx) (string, bool) { + legacyPath := fmt.Sprintf("%s/%s", ctx.Paths.LegacyDnote, consts.ConfigFilename) + + ok, err := utils.FileExists(legacyPath) + if err != nil { + log.Errorf(errors.Wrapf(err, "checking legacy dnote directory at %s", legacyPath).Error()) + } + if ok { + return legacyPath, true + } + + return "", false +} + // GetPath returns the path to the dnote config file func GetPath(ctx context.DnoteCtx) string { - return fmt.Sprintf("%s/%s", ctx.DnoteDir, consts.ConfigFilename) + legacyPath, ok := checkLegacyPath(ctx) + if ok { + return legacyPath + } + + return fmt.Sprintf("%s/%s/%s", ctx.Paths.Config, consts.DnoteDirName, consts.ConfigFilename) } // Read reads the config file diff --git a/pkg/cli/consts/consts.go b/pkg/cli/consts/consts.go index 4c84d1ae..e7235934 100644 --- a/pkg/cli/consts/consts.go +++ b/pkg/cli/consts/consts.go @@ -20,8 +20,10 @@ package consts var ( + // LegacyDnoteDirName is the name of the legacy directory containing dnote files + LegacyDnoteDirName = ".dnote" // DnoteDirName is the name of the directory containing dnote files - DnoteDirName = ".dnote" + DnoteDirName = "dnote" // DnoteDBFileName is a filename for the Dnote SQLite database DnoteDBFileName = "dnote.db" // TmpContentFileBase is the base for the filename for a temporary content diff --git a/pkg/cli/context/ctx.go b/pkg/cli/context/ctx.go index 26ddc0e2..6ba8f9dd 100644 --- a/pkg/cli/context/ctx.go +++ b/pkg/cli/context/ctx.go @@ -24,10 +24,18 @@ import ( "github.com/dnote/dnote/pkg/clock" ) +// Paths contain directory definitions +type Paths struct { + Home string + Config string + Data string + Cache string + LegacyDnote string +} + // DnoteCtx is a context holding the information of the current runtime type DnoteCtx struct { - HomeDir string - DnoteDir string + Paths Paths APIEndpoint string Version string DB *database.DB diff --git a/pkg/cli/context/testutils.go b/pkg/cli/context/testutils.go index 383c5a08..4572a62c 100644 --- a/pkg/cli/context/testutils.go +++ b/pkg/cli/context/testutils.go @@ -30,16 +30,15 @@ import ( ) // InitTestCtx initializes a test context -func InitTestCtx(t *testing.T, dnoteDir string, dbOpts *database.TestDBOptions) DnoteCtx { - dbPath := fmt.Sprintf("%s/%s", dnoteDir, consts.DnoteDBFileName) +func InitTestCtx(t *testing.T, paths Paths, dbOpts *database.TestDBOptions) DnoteCtx { + dbPath := fmt.Sprintf("%s/%s/%s", paths.Data, consts.DnoteDirName, consts.DnoteDBFileName) db := database.InitTestDB(t, dbPath, dbOpts) return DnoteCtx{ - DB: db, - DnoteDir: dnoteDir, - // Use a mock clock to test times - Clock: clock.NewMock(), + DB: db, + Paths: paths, + Clock: clock.NewMock(), // Use a mock clock to test times } } @@ -47,7 +46,13 @@ func InitTestCtx(t *testing.T, dnoteDir string, dbOpts *database.TestDBOptions) func TeardownTestCtx(t *testing.T, ctx DnoteCtx) { database.TeardownTestDB(t, ctx.DB) - if err := os.RemoveAll(ctx.DnoteDir); err != nil { - t.Fatal(errors.Wrap(err, "removing test dnote directory")) + if err := os.RemoveAll(ctx.Paths.Data); err != nil { + t.Fatal(errors.Wrap(err, "removing test data directory")) + } + if err := os.RemoveAll(ctx.Paths.Config); err != nil { + t.Fatal(errors.Wrap(err, "removing test config directory")) + } + if err := os.RemoveAll(ctx.Paths.Cache); err != nil { + t.Fatal(errors.Wrap(err, "removing test cache directory")) } } diff --git a/pkg/cli/database/testutils.go b/pkg/cli/database/testutils.go index 17032696..dbcb0bc5 100644 --- a/pkg/cli/database/testutils.go +++ b/pkg/cli/database/testutils.go @@ -151,7 +151,7 @@ func TeardownTestDB(t *testing.T, db *DB) { // OpenTestDB opens the database connection to a test database // without initializing any schema func OpenTestDB(t *testing.T, dnoteDir string) *DB { - dbPath := fmt.Sprintf("%s/%s", dnoteDir, consts.DnoteDBFileName) + dbPath := fmt.Sprintf("%s/%s/%s", dnoteDir, consts.DnoteDirName, consts.DnoteDBFileName) db, err := Open(dbPath) if err != nil { t.Fatal(errors.Wrap(err, "opening database connection to the test database")) diff --git a/pkg/cli/dirs/dirs.go b/pkg/cli/dirs/dirs.go new file mode 100644 index 00000000..81f2e5f7 --- /dev/null +++ b/pkg/cli/dirs/dirs.go @@ -0,0 +1,49 @@ +// Package dirs provides base directory definitions for the system +package dirs + +import ( + "os" + "os/user" + + "github.com/pkg/errors" +) + +var ( + // Home is the home directory of the user + Home string + // ConfigHome is the full path to the directory in which user-specific + // configurations should be written. + ConfigHome string + // DataHome is the full path to the directory in which user-specific data + // files should be written. + DataHome string + // CacheHome is the full path to the directory in which user-specific + // non-essential cached data should be writte + CacheHome string +) + +func init() { + Reload() +} + +// Reload reloads the directory definitions +func Reload() { + initDirs() +} + +func getHomeDir() string { + usr, err := user.Current() + if err != nil { + panic(errors.Wrap(err, "getting home dir")) + } + + return usr.HomeDir +} + +func readPath(envName, defaultPath string) string { + if dir := os.Getenv(envName); dir != "" { + return dir + } + + return defaultPath +} diff --git a/pkg/cli/dirs/dirs_test.go b/pkg/cli/dirs/dirs_test.go new file mode 100644 index 00000000..058b7949 --- /dev/null +++ b/pkg/cli/dirs/dirs_test.go @@ -0,0 +1,25 @@ +package dirs + +import ( + "os" + "testing" + + "github.com/dnote/dnote/pkg/assert" +) + +type envTestCase struct { + envKey string + envVal string + got *string + expected string +} + +func testCustomDirs(t *testing.T, testCases []envTestCase) { + for _, tc := range testCases { + os.Setenv(tc.envKey, tc.envVal) + + Reload() + + assert.Equal(t, *tc.got, tc.expected, "result mismatch") + } +} diff --git a/pkg/cli/dirs/dirs_unix.go b/pkg/cli/dirs/dirs_unix.go new file mode 100644 index 00000000..28dfe5fb --- /dev/null +++ b/pkg/cli/dirs/dirs_unix.go @@ -0,0 +1,33 @@ +// +build linux darwin + +package dirs + +import ( + "path/filepath" +) + +// The environment variable names for the XDG base directory specification +var ( + envConfigHome = "XDG_CONFIG_HOME" + envDataHome = "XDG_DATA_HOME" + envCacheHome = "XDG_CACHE_HOME" +) + +func initDirs() { + Home = getHomeDir() + ConfigHome = readPath(envConfigHome, getConfigHome(Home)) + DataHome = readPath(envDataHome, getDataHome(Home)) + CacheHome = readPath(envCacheHome, getCacheHome(Home)) +} + +func getConfigHome(homeDir string) string { + return filepath.Join(homeDir, ".config") +} + +func getDataHome(homeDir string) string { + return filepath.Join(homeDir, ".local/share") +} + +func getCacheHome(homeDir string) string { + return filepath.Join(homeDir, ".cache") +} diff --git a/pkg/cli/dirs/dirs_unix_test.go b/pkg/cli/dirs/dirs_unix_test.go new file mode 100644 index 00000000..a0b08c0c --- /dev/null +++ b/pkg/cli/dirs/dirs_unix_test.go @@ -0,0 +1,66 @@ +// +build linux darwin + +package dirs + +import ( + "path/filepath" + "testing" + + "github.com/dnote/dnote/pkg/assert" +) + +func TestDirs(t *testing.T) { + home := Home + assert.NotEqual(t, home, "", "home is empty") + + configHome := filepath.Join(home, ".config") + dataHome := filepath.Join(home, ".local", "share") + cacheHome := filepath.Join(home, ".cache") + + testCases := []struct { + got string + expected string + }{ + { + got: ConfigHome, + expected: configHome, + }, + { + got: DataHome, + expected: dataHome, + }, + { + got: CacheHome, + expected: cacheHome, + }, + } + + for _, tc := range testCases { + assert.Equal(t, tc.got, tc.expected, "result mismatch") + } +} + +func TestCustomDirs(t *testing.T) { + testCases := []envTestCase{ + { + envKey: "XDG_CONFIG_HOME", + envVal: "~/custom/config", + got: &ConfigHome, + expected: "~/custom/config", + }, + { + envKey: "XDG_DATA_HOME", + envVal: "~/custom/data", + got: &DataHome, + expected: "~/custom/data", + }, + { + envKey: "XDG_CACHE_HOME", + envVal: "~/custom/cache", + got: &CacheHome, + expected: "~/custom/cache", + }, + } + + testCustomDirs(t, testCases) +} diff --git a/pkg/cli/dirs/dirs_windows.go b/pkg/cli/dirs/dirs_windows.go new file mode 100644 index 00000000..a3660e18 --- /dev/null +++ b/pkg/cli/dirs/dirs_windows.go @@ -0,0 +1,14 @@ +// +build windows + +package dirs + +import ( + "path/filepath" +) + +func initDirs() { + Home = getHomeDir() + ConfigHome = filepath.Join(Home, ".dnote") + DataHome = filepath.Join(Home, ".dnote") + CacheHome = filepath.Join(Home, ".dnote") +} diff --git a/pkg/cli/dirs/dirs_windows_test.go b/pkg/cli/dirs/dirs_windows_test.go new file mode 100644 index 00000000..093c1608 --- /dev/null +++ b/pkg/cli/dirs/dirs_windows_test.go @@ -0,0 +1,41 @@ +// +build windows + +package dirs + +import ( + "path/filepath" + "testing" + + "github.com/dnote/dnote/pkg/assert" +) + +func TestDirs(t *testing.T) { + home := Home + assert.NotEqual(t, home, "", "home is empty") + + configHome := filepath.Join(home, ".dnote") + dataHome := filepath.Join(home, ".dnote") + cacheHome := filepath.Join(home, ".dnote") + + testCases := []struct { + got string + expected string + }{ + { + got: ConfigHome, + expected: configHome, + }, + { + got: DataHome, + expected: dataHome, + }, + { + got: CacheHome, + expected: cacheHome, + }, + } + + for _, tc := range testCases { + assert.Equal(t, tc.got, tc.expected, "result mismatch") + } +} diff --git a/pkg/cli/infra/init.go b/pkg/cli/infra/init.go index 5feb4b7d..381483a3 100644 --- a/pkg/cli/infra/init.go +++ b/pkg/cli/infra/init.go @@ -24,7 +24,7 @@ import ( "database/sql" "fmt" "os" - "os/user" + "path/filepath" "strconv" "time" @@ -32,6 +32,7 @@ import ( "github.com/dnote/dnote/pkg/cli/consts" "github.com/dnote/dnote/pkg/cli/context" "github.com/dnote/dnote/pkg/cli/database" + "github.com/dnote/dnote/pkg/cli/dirs" "github.com/dnote/dnote/pkg/cli/log" "github.com/dnote/dnote/pkg/cli/migrate" "github.com/dnote/dnote/pkg/cli/utils" @@ -43,24 +44,50 @@ import ( // RunEFunc is a function type of dnote commands type RunEFunc func(*cobra.Command, []string) error -func newCtx(versionTag string) (context.DnoteCtx, error) { - homeDir, err := getHomeDir() - if err != nil { - return context.DnoteCtx{}, errors.Wrap(err, "Failed to get home dir") +func checkLegacyDBPath() (string, bool) { + legacyDnoteDir := getLegacyDnotePath(dirs.Home) + ok, err := utils.FileExists(legacyDnoteDir) + if ok { + return legacyDnoteDir, true } - dnoteDir := getDnoteDir(homeDir) - dnoteDBPath := fmt.Sprintf("%s/%s", dnoteDir, consts.DnoteDBFileName) - db, err := database.Open(dnoteDBPath) + if err != nil { + log.Errorf(errors.Wrapf(err, "checking legacy dnote directory at %s", legacyDnoteDir).Error()) + } + + return "", false +} + +func getDBPath(paths context.Paths) string { + legacyDnoteDir, ok := checkLegacyDBPath() + if ok { + return fmt.Sprintf("%s/%s", legacyDnoteDir, consts.DnoteDBFileName) + } + + return fmt.Sprintf("%s/%s/%s", paths.Data, consts.DnoteDirName, consts.DnoteDBFileName) +} + +func newCtx(versionTag string) (context.DnoteCtx, error) { + dnoteDir := getLegacyDnotePath(dirs.Home) + paths := context.Paths{ + Home: dirs.Home, + Config: dirs.ConfigHome, + Data: dirs.DataHome, + Cache: dirs.CacheHome, + LegacyDnote: dnoteDir, + } + + dbPath := getDBPath(paths) + + db, err := database.Open(dbPath) if err != nil { return context.DnoteCtx{}, errors.Wrap(err, "conntecting to db") } ctx := context.DnoteCtx{ - HomeDir: homeDir, - DnoteDir: dnoteDir, - Version: versionTag, - DB: db, + Paths: paths, + Version: versionTag, + DB: db, } return ctx, nil @@ -123,8 +150,7 @@ func SetupCtx(ctx context.DnoteCtx) (context.DnoteCtx, error) { } ret := context.DnoteCtx{ - HomeDir: ctx.HomeDir, - DnoteDir: ctx.DnoteDir, + Paths: ctx.Paths, Version: ctx.Version, DB: ctx.DB, SessionKey: sessionKey, @@ -137,31 +163,10 @@ func SetupCtx(ctx context.DnoteCtx) (context.DnoteCtx, error) { return ret, nil } -func getDnoteDir(homeDir string) string { - var ret string - - dnoteDirEnv := os.Getenv("DNOTE_DIR") - if dnoteDirEnv == "" { - ret = fmt.Sprintf("%s/%s", homeDir, consts.DnoteDirName) - } else { - ret = dnoteDirEnv - } - - return ret -} - -func getHomeDir() (string, error) { - homeDirEnv := os.Getenv("DNOTE_HOME_DIR") - if homeDirEnv != "" { - return homeDirEnv, nil - } - - usr, err := user.Current() - if err != nil { - return "", errors.Wrap(err, "Failed to get current user") - } - - return usr.HomeDir, nil +// getLegacyDnotePath returns a legacy dnote directory path placed under +// the user's home directory +func getLegacyDnotePath(homeDir string) string { + return fmt.Sprintf("%s/%s", homeDir, consts.LegacyDnoteDirName) } // InitDB initializes the database. @@ -304,20 +309,32 @@ func getEditorCommand() string { return ret } -// initDnoteDir initializes dnote directory if it does not exist yet -func initDnoteDir(ctx context.DnoteCtx) error { - path := ctx.DnoteDir - +func initDir(path string) error { ok, err := utils.FileExists(path) if err != nil { - return errors.Wrap(err, "checking if dnote dir exists") + return errors.Wrapf(err, "checking if dir exists at %s", path) } if ok { return nil } if err := os.MkdirAll(path, 0755); err != nil { - return errors.Wrap(err, "Failed to create dnote directory") + return errors.Wrapf(err, "creating a directory at %s", path) + } + + return nil +} + +// initDnoteDir initializes missing directories that Dnote uses +func initDnoteDir(ctx context.DnoteCtx) error { + if err := initDir(filepath.Join(ctx.Paths.Config, consts.DnoteDirName)); err != nil { + return errors.Wrap(err, "initializing config dir") + } + if err := initDir(filepath.Join(ctx.Paths.Data, consts.DnoteDirName)); err != nil { + return errors.Wrap(err, "initializing data dir") + } + if err := initDir(filepath.Join(ctx.Paths.Cache, consts.DnoteDirName)); err != nil { + return errors.Wrap(err, "initializing cache dir") } return nil diff --git a/pkg/cli/main_test.go b/pkg/cli/main_test.go index ecf819af..38b8bdcf 100644 --- a/pkg/cli/main_test.go +++ b/pkg/cli/main_test.go @@ -35,9 +35,14 @@ import ( var binaryName = "test-dnote" +var testDir = "./tmp/.dnote" + var opts = testutils.RunDnoteCmdOptions{ - HomeDir: "./tmp", - DnoteDir: "./tmp/.dnote", + Env: []string{ + fmt.Sprintf("XDG_CONFIG_HOME=%s", testDir), + fmt.Sprintf("XDG_DATA_HOME=%s", testDir), + fmt.Sprintf("XDG_CACHE_HOME=%s", testDir), + }, } func TestMain(m *testing.M) { @@ -53,12 +58,12 @@ func TestInit(t *testing.T) { // Execute // run an arbitrary command "view" due to https://github.com/spf13/cobra/issues/1056 testutils.RunDnoteCmd(t, opts, binaryName, "view") - defer testutils.RemoveDir(t, opts.HomeDir) + defer testutils.RemoveDir(t, testDir) - db := database.OpenTestDB(t, opts.DnoteDir) + db := database.OpenTestDB(t, testDir) // Test - ok, err := utils.FileExists(opts.DnoteDir) + ok, err := utils.FileExists(testDir) if err != nil { t.Fatal(errors.Wrap(err, "checking if dnote dir exists")) } @@ -66,7 +71,7 @@ func TestInit(t *testing.T) { t.Errorf("dnote directory was not initialized") } - ok, err = utils.FileExists(fmt.Sprintf("%s/%s", opts.DnoteDir, consts.ConfigFilename)) + ok, err = utils.FileExists(fmt.Sprintf("%s/%s/%s", testDir, consts.DnoteDirName, consts.ConfigFilename)) if err != nil { t.Fatal(errors.Wrap(err, "checking if dnote config exists")) } @@ -104,9 +109,9 @@ func TestAddNote(t *testing.T) { t.Run("new book", func(t *testing.T) { // Set up and execute testutils.RunDnoteCmd(t, opts, binaryName, "add", "js", "-c", "foo") - defer testutils.RemoveDir(t, opts.HomeDir) + defer testutils.RemoveDir(t, testDir) - db := database.OpenTestDB(t, opts.DnoteDir) + db := database.OpenTestDB(t, testDir) // Test var noteCount, bookCount int @@ -132,12 +137,12 @@ func TestAddNote(t *testing.T) { t.Run("existing book", func(t *testing.T) { // Setup - db := database.InitTestDB(t, fmt.Sprintf("%s/%s", opts.DnoteDir, consts.DnoteDBFileName), nil) + db := database.InitTestDB(t, fmt.Sprintf("%s/%s/%s", testDir, consts.DnoteDirName, consts.DnoteDBFileName), nil) testutils.Setup3(t, db) // Execute testutils.RunDnoteCmd(t, opts, binaryName, "add", "js", "-c", "foo") - defer testutils.RemoveDir(t, opts.HomeDir) + defer testutils.RemoveDir(t, testDir) // Test @@ -173,12 +178,12 @@ func TestAddNote(t *testing.T) { func TestEditNote(t *testing.T) { t.Run("content flag", func(t *testing.T) { // Setup - db := database.InitTestDB(t, fmt.Sprintf("%s/%s", opts.DnoteDir, consts.DnoteDBFileName), nil) + db := database.InitTestDB(t, fmt.Sprintf("%s/%s/%s", testDir, consts.DnoteDirName, consts.DnoteDBFileName), nil) testutils.Setup4(t, db) // Execute testutils.RunDnoteCmd(t, opts, binaryName, "edit", "2", "-c", "foo bar") - defer testutils.RemoveDir(t, opts.HomeDir) + defer testutils.RemoveDir(t, testDir) // Test var noteCount, bookCount int @@ -206,12 +211,12 @@ func TestEditNote(t *testing.T) { t.Run("book flag", func(t *testing.T) { // Setup - db := database.InitTestDB(t, fmt.Sprintf("%s/%s", opts.DnoteDir, consts.DnoteDBFileName), nil) + db := database.InitTestDB(t, fmt.Sprintf("%s/%s/%s", testDir, consts.DnoteDirName, consts.DnoteDBFileName), nil) testutils.Setup5(t, db) // Execute testutils.RunDnoteCmd(t, opts, binaryName, "edit", "2", "-b", "linux") - defer testutils.RemoveDir(t, opts.HomeDir) + defer testutils.RemoveDir(t, testDir) // Test var noteCount, bookCount int @@ -240,12 +245,12 @@ func TestEditNote(t *testing.T) { t.Run("book flag and content flag", func(t *testing.T) { // Setup - db := database.InitTestDB(t, fmt.Sprintf("%s/%s", opts.DnoteDir, consts.DnoteDBFileName), nil) + db := database.InitTestDB(t, fmt.Sprintf("%s/%s/%s", testDir, consts.DnoteDirName, consts.DnoteDBFileName), nil) testutils.Setup5(t, db) // Execute testutils.RunDnoteCmd(t, opts, binaryName, "edit", "2", "-b", "linux", "-c", "n2 body updated") - defer testutils.RemoveDir(t, opts.HomeDir) + defer testutils.RemoveDir(t, testDir) // Test var noteCount, bookCount int @@ -276,12 +281,12 @@ func TestEditNote(t *testing.T) { func TestEditBook(t *testing.T) { t.Run("name flag", func(t *testing.T) { // Setup - db := database.InitTestDB(t, fmt.Sprintf("%s/%s", opts.DnoteDir, consts.DnoteDBFileName), nil) + db := database.InitTestDB(t, fmt.Sprintf("%s/%s/%s", testDir, consts.DnoteDirName, consts.DnoteDBFileName), nil) testutils.Setup1(t, db) // Execute testutils.RunDnoteCmd(t, opts, binaryName, "edit", "js", "-n", "js-edited") - defer testutils.RemoveDir(t, opts.HomeDir) + defer testutils.RemoveDir(t, testDir) // Test var noteCount, bookCount int @@ -335,7 +340,7 @@ func TestRemoveNote(t *testing.T) { for _, tc := range testCases { t.Run(fmt.Sprintf("--yes=%t", tc.yesFlag), func(t *testing.T) { // Setup - db := database.InitTestDB(t, fmt.Sprintf("%s/%s", opts.DnoteDir, consts.DnoteDBFileName), nil) + db := database.InitTestDB(t, fmt.Sprintf("%s/%s/%s", testDir, consts.DnoteDirName, consts.DnoteDBFileName), nil) testutils.Setup2(t, db) // Execute @@ -344,7 +349,7 @@ func TestRemoveNote(t *testing.T) { } else { testutils.WaitDnoteCmd(t, opts, testutils.UserConfirm, binaryName, "remove", "1") } - defer testutils.RemoveDir(t, opts.HomeDir) + defer testutils.RemoveDir(t, testDir) // Test var noteCount, bookCount, jsNoteCount, linuxNoteCount int @@ -422,7 +427,7 @@ func TestRemoveBook(t *testing.T) { for _, tc := range testCases { t.Run(fmt.Sprintf("--yes=%t", tc.yesFlag), func(t *testing.T) { // Setup - db := database.InitTestDB(t, fmt.Sprintf("%s/%s", opts.DnoteDir, consts.DnoteDBFileName), nil) + db := database.InitTestDB(t, fmt.Sprintf("%s/%s/%s", testDir, consts.DnoteDirName, consts.DnoteDBFileName), nil) testutils.Setup2(t, db) // Execute @@ -432,7 +437,7 @@ func TestRemoveBook(t *testing.T) { testutils.WaitDnoteCmd(t, opts, testutils.UserConfirm, binaryName, "remove", "js") } - defer testutils.RemoveDir(t, opts.HomeDir) + defer testutils.RemoveDir(t, testDir) // Test var noteCount, bookCount, jsNoteCount, linuxNoteCount int diff --git a/pkg/cli/migrate/legacy.go b/pkg/cli/migrate/legacy.go index 3d771ac9..ef4f8045 100644 --- a/pkg/cli/migrate/legacy.go +++ b/pkg/cli/migrate/legacy.go @@ -177,8 +177,8 @@ func performMigration(ctx context.DnoteCtx, migrationID int) error { // backupDnoteDir backs up the dnote directory to a temporary backup directory func backupDnoteDir(ctx context.DnoteCtx) error { - srcPath := fmt.Sprintf("%s/.dnote", ctx.HomeDir) - tmpPath := fmt.Sprintf("%s/%s", ctx.HomeDir, backupDirName) + srcPath := fmt.Sprintf("%s/.dnote", ctx.Paths.Home) + tmpPath := fmt.Sprintf("%s/%s", ctx.Paths.Home, backupDirName) if err := utils.CopyDir(srcPath, tmpPath); err != nil { return errors.Wrap(err, "Failed to copy the .dnote directory") @@ -198,8 +198,8 @@ func restoreBackup(ctx context.DnoteCtx) error { } }() - srcPath := fmt.Sprintf("%s/.dnote", ctx.HomeDir) - backupPath := fmt.Sprintf("%s/%s", ctx.HomeDir, backupDirName) + srcPath := fmt.Sprintf("%s/.dnote", ctx.Paths.Home) + backupPath := fmt.Sprintf("%s/%s", ctx.Paths.Home, backupDirName) if err = os.RemoveAll(srcPath); err != nil { return errors.Wrapf(err, "Failed to clear current dnote data at %s", backupPath) @@ -213,7 +213,7 @@ func restoreBackup(ctx context.DnoteCtx) error { } func clearBackup(ctx context.DnoteCtx) error { - backupPath := fmt.Sprintf("%s/%s", ctx.HomeDir, backupDirName) + backupPath := fmt.Sprintf("%s/%s", ctx.Paths.Home, backupDirName) if err := os.RemoveAll(backupPath); err != nil { return errors.Wrapf(err, "Failed to remove backup at %s", backupPath) @@ -224,7 +224,7 @@ func clearBackup(ctx context.DnoteCtx) error { // getSchemaPath returns the path to the file containing schema info func getSchemaPath(ctx context.DnoteCtx) string { - return fmt.Sprintf("%s/%s", ctx.DnoteDir, schemaFilename) + return fmt.Sprintf("%s/%s", ctx.Paths.LegacyDnote, schemaFilename) } func readSchema(ctx context.DnoteCtx) (schema, error) { @@ -485,7 +485,7 @@ var migrateToV8SystemKeyBookMark = "bookmark" // migrateToV1 deletes YAML archive if exists func migrateToV1(ctx context.DnoteCtx) error { - yamlPath := fmt.Sprintf("%s/%s", ctx.HomeDir, ".dnote-yaml-archived") + yamlPath := fmt.Sprintf("%s/%s", ctx.Paths.Home, ".dnote-yaml-archived") ok, err := utils.FileExists(yamlPath) if err != nil { return errors.Wrap(err, "checking if yaml file exists") @@ -502,7 +502,7 @@ func migrateToV1(ctx context.DnoteCtx) error { } func migrateToV2(ctx context.DnoteCtx) error { - notePath := fmt.Sprintf("%s/dnote", ctx.DnoteDir) + notePath := fmt.Sprintf("%s/dnote", ctx.Paths.LegacyDnote) b, err := ioutil.ReadFile(notePath) if err != nil { @@ -558,8 +558,8 @@ func migrateToV2(ctx context.DnoteCtx) error { // migrateToV3 generates actions for existing dnote func migrateToV3(ctx context.DnoteCtx) error { - notePath := fmt.Sprintf("%s/dnote", ctx.DnoteDir) - actionsPath := fmt.Sprintf("%s/actions", ctx.DnoteDir) + notePath := fmt.Sprintf("%s/dnote", ctx.Paths.LegacyDnote) + actionsPath := fmt.Sprintf("%s/actions", ctx.Paths.LegacyDnote) b, err := ioutil.ReadFile(notePath) if err != nil { @@ -645,7 +645,7 @@ func getEditorCommand() string { } func migrateToV4(ctx context.DnoteCtx) error { - configPath := fmt.Sprintf("%s/dnoterc", ctx.DnoteDir) + configPath := fmt.Sprintf("%s/dnoterc", ctx.Paths.LegacyDnote) b, err := ioutil.ReadFile(configPath) if err != nil { @@ -678,7 +678,7 @@ func migrateToV4(ctx context.DnoteCtx) error { // migrateToV5 migrates actions func migrateToV5(ctx context.DnoteCtx) error { - actionsPath := fmt.Sprintf("%s/actions", ctx.DnoteDir) + actionsPath := fmt.Sprintf("%s/actions", ctx.Paths.LegacyDnote) b, err := ioutil.ReadFile(actionsPath) if err != nil { @@ -748,7 +748,7 @@ func migrateToV5(ctx context.DnoteCtx) error { // migrateToV6 adds a 'public' field to notes func migrateToV6(ctx context.DnoteCtx) error { - notePath := fmt.Sprintf("%s/dnote", ctx.DnoteDir) + notePath := fmt.Sprintf("%s/dnote", ctx.Paths.LegacyDnote) b, err := ioutil.ReadFile(notePath) if err != nil { @@ -803,7 +803,7 @@ func migrateToV6(ctx context.DnoteCtx) error { // EditNoteDataV2. Due to a bug, edit logged actions with schema version '2' // but with a data of EditNoteDataV1. https://github.com/dnote/dnote/pkg/cli/issues/107 func migrateToV7(ctx context.DnoteCtx) error { - actionPath := fmt.Sprintf("%s/actions", ctx.DnoteDir) + actionPath := fmt.Sprintf("%s/actions", ctx.Paths.LegacyDnote) b, err := ioutil.ReadFile(actionPath) if err != nil { @@ -873,7 +873,7 @@ func migrateToV8(ctx context.DnoteCtx) error { } // 1. Migrate the the dnote file - dnoteFilePath := fmt.Sprintf("%s/dnote", ctx.DnoteDir) + dnoteFilePath := fmt.Sprintf("%s/dnote", ctx.Paths.LegacyDnote) b, err := ioutil.ReadFile(dnoteFilePath) if err != nil { return errors.Wrap(err, "reading the notes") @@ -913,7 +913,7 @@ func migrateToV8(ctx context.DnoteCtx) error { } // 2. Migrate the actions file - actionsPath := fmt.Sprintf("%s/actions", ctx.DnoteDir) + actionsPath := fmt.Sprintf("%s/actions", ctx.Paths.LegacyDnote) b, err = ioutil.ReadFile(actionsPath) if err != nil { return errors.Wrap(err, "reading the actions") @@ -938,7 +938,7 @@ func migrateToV8(ctx context.DnoteCtx) error { } // 3. Migrate the timestamps file - timestampsPath := fmt.Sprintf("%s/timestamps", ctx.DnoteDir) + timestampsPath := fmt.Sprintf("%s/timestamps", ctx.Paths.LegacyDnote) b, err = ioutil.ReadFile(timestampsPath) if err != nil { return errors.Wrap(err, "reading the timestamps") @@ -980,7 +980,7 @@ func migrateToV8(ctx context.DnoteCtx) error { if err := os.RemoveAll(timestampsPath); err != nil { return errors.Wrap(err, "removing the timestamps file") } - schemaPath := fmt.Sprintf("%s/schema", ctx.DnoteDir) + schemaPath := fmt.Sprintf("%s/schema", ctx.Paths.LegacyDnote) if err := os.RemoveAll(schemaPath); err != nil { return errors.Wrap(err, "removing the schema file") } diff --git a/pkg/cli/migrate/legacy_test.go b/pkg/cli/migrate/legacy_test.go index 7fe81126..948dd7b4 100644 --- a/pkg/cli/migrate/legacy_test.go +++ b/pkg/cli/migrate/legacy_test.go @@ -42,13 +42,15 @@ func setupEnv(t *testing.T, homeDir string) context.DnoteCtx { } return context.DnoteCtx{ - HomeDir: homeDir, - DnoteDir: dnoteDir, + Paths: context.Paths{ + Home: homeDir, + LegacyDnote: dnoteDir, + }, } } func teardownEnv(t *testing.T, ctx context.DnoteCtx) { - if err := os.RemoveAll(ctx.DnoteDir); err != nil { + if err := os.RemoveAll(ctx.Paths.LegacyDnote); err != nil { t.Fatal(errors.Wrap(err, "tearing down the dnote dir")) } } @@ -59,7 +61,7 @@ func TestMigrateToV1(t *testing.T) { ctx := setupEnv(t, "../tmp") defer teardownEnv(t, ctx) - yamlPath, err := filepath.Abs(filepath.Join(ctx.HomeDir, ".dnote-yaml-archived")) + yamlPath, err := filepath.Abs(filepath.Join(ctx.Paths.Home, ".dnote-yaml-archived")) if err != nil { panic(errors.Wrap(err, "Failed to get absolute YAML path").Error()) } @@ -85,7 +87,7 @@ func TestMigrateToV1(t *testing.T) { ctx := setupEnv(t, "../tmp") defer teardownEnv(t, ctx) - yamlPath, err := filepath.Abs(filepath.Join(ctx.HomeDir, ".dnote-yaml-archived")) + yamlPath, err := filepath.Abs(filepath.Join(ctx.Paths.Home, ".dnote-yaml-archived")) if err != nil { panic(errors.Wrap(err, "Failed to get absolute YAML path").Error()) } @@ -356,7 +358,13 @@ func TestMigrateToV8(t *testing.T) { db := database.InitTestDB(t, "../tmp/.dnote/dnote-test.db", &opts) defer database.TeardownTestDB(t, db) - ctx := context.DnoteCtx{HomeDir: "../tmp", DnoteDir: "../tmp/.dnote", DB: db} + ctx := context.DnoteCtx{ + Paths: context.Paths{ + Home: "../tmp", + LegacyDnote: "../tmp/.dnote", + }, + DB: db, + } // set up testutils.CopyFixture(t, ctx, "./fixtures/legacy-8-actions.json", "actions") @@ -373,10 +381,10 @@ func TestMigrateToV8(t *testing.T) { // test // 1. test if files are migrated - dnoteFilePath := fmt.Sprintf("%s/dnote", ctx.DnoteDir) - dnotercPath := fmt.Sprintf("%s/dnoterc", ctx.DnoteDir) - schemaFilePath := fmt.Sprintf("%s/schema", ctx.DnoteDir) - timestampFilePath := fmt.Sprintf("%s/timestamps", ctx.DnoteDir) + dnoteFilePath := fmt.Sprintf("%s/dnote", ctx.Paths.LegacyDnote) + dnotercPath := fmt.Sprintf("%s/dnoterc", ctx.Paths.LegacyDnote) + schemaFilePath := fmt.Sprintf("%s/schema", ctx.Paths.LegacyDnote) + timestampFilePath := fmt.Sprintf("%s/timestamps", ctx.Paths.LegacyDnote) ok, err := utils.FileExists(dnoteFilePath) if err != nil { diff --git a/pkg/cli/migrate/migrate_test.go b/pkg/cli/migrate/migrate_test.go index 2a94b9d6..6a2719b0 100644 --- a/pkg/cli/migrate/migrate_test.go +++ b/pkg/cli/migrate/migrate_test.go @@ -37,6 +37,14 @@ import ( "github.com/pkg/errors" ) +var paths context.Paths = context.Paths{ + Home: "../../tmp", + Cache: "../../tmp", + Config: "../../tmp", + Data: "../../tmp", + LegacyDnote: "../../tmp", +} + func TestExecute_bump_schema(t *testing.T) { testCases := []struct { schemaKey string @@ -53,7 +61,7 @@ func TestExecute_bump_schema(t *testing.T) { func() { // set up opts := database.TestDBOptions{SkipMigration: true} - ctx := context.InitTestCtx(t, "../tmp", &opts) + ctx := context.InitTestCtx(t, paths, &opts) defer context.TeardownTestCtx(t, ctx) db := ctx.DB @@ -110,7 +118,7 @@ func TestRun_nonfresh(t *testing.T) { func() { // set up opts := database.TestDBOptions{SkipMigration: true} - ctx := context.InitTestCtx(t, "../tmp", &opts) + ctx := context.InitTestCtx(t, paths, &opts) defer context.TeardownTestCtx(t, ctx) db := ctx.DB @@ -190,7 +198,7 @@ func TestRun_fresh(t *testing.T) { func() { // set up opts := database.TestDBOptions{SkipMigration: true} - ctx := context.InitTestCtx(t, "../tmp", &opts) + ctx := context.InitTestCtx(t, paths, &opts) defer context.TeardownTestCtx(t, ctx) db := ctx.DB @@ -264,7 +272,7 @@ func TestRun_up_to_date(t *testing.T) { func() { // set up opts := database.TestDBOptions{SkipMigration: true} - ctx := context.InitTestCtx(t, "../tmp", &opts) + ctx := context.InitTestCtx(t, paths, &opts) defer context.TeardownTestCtx(t, ctx) db := ctx.DB @@ -319,7 +327,7 @@ func TestRun_up_to_date(t *testing.T) { func TestLocalMigration1(t *testing.T) { // set up opts := database.TestDBOptions{SchemaSQLPath: "./fixtures/local-1-pre-schema.sql", SkipMigration: true} - ctx := context.InitTestCtx(t, "../tmp", &opts) + ctx := context.InitTestCtx(t, paths, &opts) defer context.TeardownTestCtx(t, ctx) db := ctx.DB @@ -397,7 +405,7 @@ func TestLocalMigration1(t *testing.T) { func TestLocalMigration2(t *testing.T) { // set up opts := database.TestDBOptions{SchemaSQLPath: "./fixtures/local-1-pre-schema.sql", SkipMigration: true} - ctx := context.InitTestCtx(t, "../tmp", &opts) + ctx := context.InitTestCtx(t, paths, &opts) defer context.TeardownTestCtx(t, ctx) db := ctx.DB @@ -484,7 +492,7 @@ func TestLocalMigration2(t *testing.T) { func TestLocalMigration3(t *testing.T) { // set up opts := database.TestDBOptions{SchemaSQLPath: "./fixtures/local-1-pre-schema.sql", SkipMigration: true} - ctx := context.InitTestCtx(t, "../tmp", &opts) + ctx := context.InitTestCtx(t, paths, &opts) defer context.TeardownTestCtx(t, ctx) db := ctx.DB @@ -559,7 +567,7 @@ func TestLocalMigration3(t *testing.T) { func TestLocalMigration4(t *testing.T) { // set up opts := database.TestDBOptions{SchemaSQLPath: "./fixtures/local-1-pre-schema.sql", SkipMigration: true} - ctx := context.InitTestCtx(t, "../tmp", &opts) + ctx := context.InitTestCtx(t, paths, &opts) defer context.TeardownTestCtx(t, ctx) db := ctx.DB @@ -603,7 +611,7 @@ func TestLocalMigration4(t *testing.T) { func TestLocalMigration5(t *testing.T) { // set up opts := database.TestDBOptions{SchemaSQLPath: "./fixtures/local-5-pre-schema.sql", SkipMigration: true} - ctx := context.InitTestCtx(t, "../tmp", &opts) + ctx := context.InitTestCtx(t, paths, &opts) defer context.TeardownTestCtx(t, ctx) db := ctx.DB @@ -665,7 +673,7 @@ func TestLocalMigration5(t *testing.T) { func TestLocalMigration6(t *testing.T) { // set up opts := database.TestDBOptions{SchemaSQLPath: "./fixtures/local-5-pre-schema.sql", SkipMigration: true} - ctx := context.InitTestCtx(t, "../tmp", &opts) + ctx := context.InitTestCtx(t, paths, &opts) defer context.TeardownTestCtx(t, ctx) db := ctx.DB @@ -698,7 +706,7 @@ func TestLocalMigration6(t *testing.T) { func TestLocalMigration7_trash(t *testing.T) { // set up opts := database.TestDBOptions{SchemaSQLPath: "./fixtures/local-7-pre-schema.sql", SkipMigration: true} - ctx := context.InitTestCtx(t, "../tmp", &opts) + ctx := context.InitTestCtx(t, paths, &opts) defer context.TeardownTestCtx(t, ctx) db := ctx.DB @@ -731,7 +739,7 @@ func TestLocalMigration7_trash(t *testing.T) { func TestLocalMigration7_conflicts(t *testing.T) { // set up opts := database.TestDBOptions{SchemaSQLPath: "./fixtures/local-7-pre-schema.sql", SkipMigration: true} - ctx := context.InitTestCtx(t, "../tmp", &opts) + ctx := context.InitTestCtx(t, paths, &opts) defer context.TeardownTestCtx(t, ctx) db := ctx.DB @@ -764,7 +772,7 @@ func TestLocalMigration7_conflicts(t *testing.T) { func TestLocalMigration7_conflicts_dup(t *testing.T) { // set up opts := database.TestDBOptions{SchemaSQLPath: "./fixtures/local-7-pre-schema.sql", SkipMigration: true} - ctx := context.InitTestCtx(t, "../tmp", &opts) + ctx := context.InitTestCtx(t, paths, &opts) defer context.TeardownTestCtx(t, ctx) db := ctx.DB @@ -802,7 +810,7 @@ func TestLocalMigration7_conflicts_dup(t *testing.T) { func TestLocalMigration8(t *testing.T) { // set up opts := database.TestDBOptions{SchemaSQLPath: "./fixtures/local-8-pre-schema.sql", SkipMigration: true} - ctx := context.InitTestCtx(t, "../tmp", &opts) + ctx := context.InitTestCtx(t, paths, &opts) defer context.TeardownTestCtx(t, ctx) db := ctx.DB @@ -868,7 +876,7 @@ func TestLocalMigration8(t *testing.T) { func TestLocalMigration9(t *testing.T) { // set up opts := database.TestDBOptions{SchemaSQLPath: "./fixtures/local-9-pre-schema.sql", SkipMigration: true} - ctx := context.InitTestCtx(t, "../tmp", &opts) + ctx := context.InitTestCtx(t, paths, &opts) defer context.TeardownTestCtx(t, ctx) db := ctx.DB @@ -914,7 +922,7 @@ func TestLocalMigration9(t *testing.T) { func TestLocalMigration10(t *testing.T) { // set up opts := database.TestDBOptions{SchemaSQLPath: "./fixtures/local-10-pre-schema.sql", SkipMigration: true} - ctx := context.InitTestCtx(t, "../tmp", &opts) + ctx := context.InitTestCtx(t, paths, &opts) defer context.TeardownTestCtx(t, ctx) db := ctx.DB @@ -986,7 +994,7 @@ func TestLocalMigration10(t *testing.T) { func TestLocalMigration11(t *testing.T) { // set up opts := database.TestDBOptions{SchemaSQLPath: "./fixtures/local-11-pre-schema.sql", SkipMigration: true} - ctx := context.InitTestCtx(t, "../tmp", &opts) + ctx := context.InitTestCtx(t, paths, &opts) defer context.TeardownTestCtx(t, ctx) db := ctx.DB @@ -1066,11 +1074,11 @@ func TestLocalMigration11(t *testing.T) { func TestLocalMigration12(t *testing.T) { // set up opts := database.TestDBOptions{SchemaSQLPath: "./fixtures/local-12-pre-schema.sql", SkipMigration: true} - ctx := context.InitTestCtx(t, "../tmp", &opts) + ctx := context.InitTestCtx(t, paths, &opts) defer context.TeardownTestCtx(t, ctx) data := []byte("editor: vim") - path := fmt.Sprintf("%s/dnoterc", ctx.DnoteDir) + path := fmt.Sprintf("%s/dnoterc", ctx.Paths.LegacyDnote) if err := ioutil.WriteFile(path, data, 0644); err != nil { t.Fatal(errors.Wrap(err, "Failed to write schema file")) } @@ -1103,7 +1111,7 @@ func TestLocalMigration12(t *testing.T) { func TestRemoteMigration1(t *testing.T) { // set up opts := database.TestDBOptions{SchemaSQLPath: "./fixtures/remote-1-pre-schema.sql", SkipMigration: true} - ctx := context.InitTestCtx(t, "../tmp", &opts) + ctx := context.InitTestCtx(t, paths, &opts) defer context.TeardownTestCtx(t, ctx) testutils.Login(t, &ctx) diff --git a/pkg/cli/testutils/main.go b/pkg/cli/testutils/main.go index 68f66eaa..dfc62421 100644 --- a/pkg/cli/testutils/main.go +++ b/pkg/cli/testutils/main.go @@ -22,7 +22,6 @@ package testutils import ( "bytes" "encoding/json" - "fmt" "io" "io/ioutil" "os" @@ -64,7 +63,7 @@ func CopyFixture(t *testing.T, ctx context.DnoteCtx, fixturePath string, filenam t.Fatal(errors.Wrap(err, "getting the absolute path for fixture")) } - dp, err := filepath.Abs(filepath.Join(ctx.DnoteDir, filename)) + dp, err := filepath.Abs(filepath.Join(ctx.Paths.LegacyDnote, filename)) if err != nil { t.Fatal(errors.Wrap(err, "getting the absolute path dnote dir")) } @@ -77,7 +76,7 @@ func CopyFixture(t *testing.T, ctx context.DnoteCtx, fixturePath string, filenam // WriteFile writes a file with the given content and filename inside the dnote dir func WriteFile(ctx context.DnoteCtx, content []byte, filename string) { - dp, err := filepath.Abs(filepath.Join(ctx.DnoteDir, filename)) + dp, err := filepath.Abs(filepath.Join(ctx.Paths.LegacyDnote, filename)) if err != nil { panic(err) } @@ -89,7 +88,7 @@ func WriteFile(ctx context.DnoteCtx, content []byte, filename string) { // ReadFile reads the content of the file with the given name in dnote dir func ReadFile(ctx context.DnoteCtx, filename string) []byte { - path := filepath.Join(ctx.DnoteDir, filename) + path := filepath.Join(ctx.Paths.LegacyDnote, filename) b, err := ioutil.ReadFile(path) if err != nil { @@ -121,17 +120,17 @@ func NewDnoteCmd(opts RunDnoteCmdOptions, binaryName string, arg ...string) (*ex } cmd := exec.Command(binaryPath, arg...) - cmd.Env = []string{fmt.Sprintf("DNOTE_DIR=%s", opts.DnoteDir), fmt.Sprintf("DNOTE_HOME_DIR=%s", opts.HomeDir)} cmd.Stderr = &stderr cmd.Stdout = &stdout + cmd.Env = opts.Env + return cmd, &stderr, &stdout, nil } // RunDnoteCmdOptions is an option for RunDnoteCmd type RunDnoteCmdOptions struct { - DnoteDir string - HomeDir string + Env []string } // RunDnoteCmd runs a dnote command diff --git a/pkg/cli/ui/editor.go b/pkg/cli/ui/editor.go index 4b5cb0d4..9fa73072 100644 --- a/pkg/cli/ui/editor.go +++ b/pkg/cli/ui/editor.go @@ -37,7 +37,7 @@ import ( func GetTmpContentPath(ctx context.DnoteCtx) (string, error) { for i := 0; ; i++ { filename := fmt.Sprintf("%s_%d.%s", consts.TmpContentFileBase, i, consts.TmpContentFileExt) - candidate := fmt.Sprintf("%s/%s", ctx.DnoteDir, filename) + candidate := fmt.Sprintf("%s/%s", ctx.Paths.Cache, filename) ok, err := utils.FileExists(candidate) if err != nil { diff --git a/pkg/cli/ui/editor_test.go b/pkg/cli/ui/editor_test.go index d89e3d9c..03338560 100644 --- a/pkg/cli/ui/editor_test.go +++ b/pkg/cli/ui/editor_test.go @@ -30,7 +30,10 @@ import ( func TestGetTmpContentPath(t *testing.T) { t.Run("no collision", func(t *testing.T) { - ctx := context.InitTestCtx(t, "../tmp1", nil) + ctx := context.InitTestCtx(t, context.Paths{ + Data: "../tmp", + Cache: "../tmp", + }, nil) defer context.TeardownTestCtx(t, ctx) res, err := GetTmpContentPath(ctx) @@ -38,16 +41,19 @@ func TestGetTmpContentPath(t *testing.T) { t.Fatal(errors.Wrap(err, "executing")) } - expected := fmt.Sprintf("%s/%s", ctx.DnoteDir, "DNOTE_TMPCONTENT_0.md") + expected := fmt.Sprintf("%s/%s", ctx.Paths.Cache, "DNOTE_TMPCONTENT_0.md") assert.Equal(t, res, expected, "filename did not match") }) t.Run("one existing session", func(t *testing.T) { // set up - ctx := context.InitTestCtx(t, "../tmp2", nil) + ctx := context.InitTestCtx(t, context.Paths{ + Data: "../tmp2", + Cache: "../tmp2", + }, nil) defer context.TeardownTestCtx(t, ctx) - p := fmt.Sprintf("%s/%s", ctx.DnoteDir, "DNOTE_TMPCONTENT_0.md") + p := fmt.Sprintf("%s/%s", ctx.Paths.Cache, "DNOTE_TMPCONTENT_0.md") if _, err := os.Create(p); err != nil { t.Fatal(errors.Wrap(err, "preparing the conflicting file")) } @@ -59,20 +65,23 @@ func TestGetTmpContentPath(t *testing.T) { } // test - expected := fmt.Sprintf("%s/%s", ctx.DnoteDir, "DNOTE_TMPCONTENT_1.md") + expected := fmt.Sprintf("%s/%s", ctx.Paths.Cache, "DNOTE_TMPCONTENT_1.md") assert.Equal(t, res, expected, "filename did not match") }) t.Run("two existing sessions", func(t *testing.T) { // set up - ctx := context.InitTestCtx(t, "../tmp3", nil) + ctx := context.InitTestCtx(t, context.Paths{ + Data: "../tmp3", + Cache: "../tmp3", + }, nil) defer context.TeardownTestCtx(t, ctx) - p1 := fmt.Sprintf("%s/%s", ctx.DnoteDir, "DNOTE_TMPCONTENT_0.md") + p1 := fmt.Sprintf("%s/%s", ctx.Paths.Cache, "DNOTE_TMPCONTENT_0.md") if _, err := os.Create(p1); err != nil { t.Fatal(errors.Wrap(err, "preparing the conflicting file")) } - p2 := fmt.Sprintf("%s/%s", ctx.DnoteDir, "DNOTE_TMPCONTENT_1.md") + p2 := fmt.Sprintf("%s/%s", ctx.Paths.Cache, "DNOTE_TMPCONTENT_1.md") if _, err := os.Create(p2); err != nil { t.Fatal(errors.Wrap(err, "preparing the conflicting file")) } @@ -84,7 +93,7 @@ func TestGetTmpContentPath(t *testing.T) { } // test - expected := fmt.Sprintf("%s/%s", ctx.DnoteDir, "DNOTE_TMPCONTENT_2.md") + expected := fmt.Sprintf("%s/%s", ctx.Paths.Cache, "DNOTE_TMPCONTENT_2.md") assert.Equal(t, res, expected, "filename did not match") }) } From a4640977b521586141234eef714de2101eed4625 Mon Sep 17 00:00:00 2001 From: Sung Won Cho Date: Sun, 3 Jan 2021 12:32:24 +1100 Subject: [PATCH 04/91] Allow to skip frontmatter when viewing note (#528) --- pkg/cli/cmd/cat/cat.go | 10 +++++++--- pkg/cli/cmd/view/view.go | 6 ++++-- pkg/cli/output/output.go | 4 ++++ 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/pkg/cli/cmd/cat/cat.go b/pkg/cli/cmd/cat/cat.go index ebc0f6a0..9adb5fe8 100644 --- a/pkg/cli/cmd/cat/cat.go +++ b/pkg/cli/cmd/cat/cat.go @@ -55,7 +55,7 @@ func NewCmd(ctx context.DnoteCtx) *cobra.Command { Aliases: []string{"c"}, Short: "See a note", Example: example, - RunE: NewRun(ctx), + RunE: NewRun(ctx, false), PreRunE: preRun, Deprecated: deprecationWarning, } @@ -64,7 +64,7 @@ func NewCmd(ctx context.DnoteCtx) *cobra.Command { } // NewRun returns a new run function -func NewRun(ctx context.DnoteCtx) infra.RunEFunc { +func NewRun(ctx context.DnoteCtx, contentOnly bool) infra.RunEFunc { return func(cmd *cobra.Command, args []string) error { var noteRowIDArg string @@ -87,7 +87,11 @@ func NewRun(ctx context.DnoteCtx) infra.RunEFunc { return err } - output.NoteInfo(info) + if contentOnly { + output.NoteContent(info) + } else { + output.NoteInfo(info) + } return nil } diff --git a/pkg/cli/cmd/view/view.go b/pkg/cli/cmd/view/view.go index 271c0c2e..1794560c 100644 --- a/pkg/cli/cmd/view/view.go +++ b/pkg/cli/cmd/view/view.go @@ -41,6 +41,7 @@ var example = ` ` var nameOnly bool +var contentOnly bool func preRun(cmd *cobra.Command, args []string) error { if len(args) > 2 { @@ -63,6 +64,7 @@ func NewCmd(ctx context.DnoteCtx) *cobra.Command { f := cmd.Flags() f.BoolVarP(&nameOnly, "name-only", "", false, "print book names only") + f.BoolVarP(&contentOnly, "content-only", "", false, "print the note content only") return cmd } @@ -79,13 +81,13 @@ func newRun(ctx context.DnoteCtx) infra.RunEFunc { } if utils.IsNumber(args[0]) { - run = cat.NewRun(ctx) + run = cat.NewRun(ctx, contentOnly) } else { run = ls.NewRun(ctx, false) } } else if len(args) == 2 { // DEPRECATED: passing book name to view command is deprecated - run = cat.NewRun(ctx) + run = cat.NewRun(ctx, false) } else { return errors.New("Incorrect number of arguments") } diff --git a/pkg/cli/output/output.go b/pkg/cli/output/output.go index a25666c9..6438b740 100644 --- a/pkg/cli/output/output.go +++ b/pkg/cli/output/output.go @@ -43,6 +43,10 @@ func NoteInfo(info database.NoteInfo) { fmt.Printf("\n-------------------------------------------------------\n") } +func NoteContent(info database.NoteInfo) { + fmt.Printf("%s", info.Content) +} + // BookInfo prints a note information func BookInfo(info database.BookInfo) { log.Infof("book name: %s\n", info.Name) From fe32dc4f2d9fd7cb19a49c87af1534e419765df7 Mon Sep 17 00:00:00 2001 From: Sung Won Cho Date: Sun, 3 Jan 2021 12:37:10 +1100 Subject: [PATCH 05/91] Release CLI 0.12.0 (#529) --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 060d176d..1d58a508 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -193,6 +193,11 @@ The following log documentes the history of the CLI project None +### 0.12.0 - 2020-01-03 + +- Use XDG base directory on Linux and macOS (#527) +- Add `--content-only` flag to print the note content only (#528) + ### 0.11.1 - 2020-04-25 #### Fixed From 6eb68d1817507d4417707eeed02aab6a0b58ff1e Mon Sep 17 00:00:00 2001 From: Sung Won Cho Date: Sun, 3 Jan 2021 14:06:38 +1100 Subject: [PATCH 06/91] Document upgrade guide (#530) --- CHANGELOG.md | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d58a508..a2cf52dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -195,9 +195,31 @@ None ### 0.12.0 - 2020-01-03 -- Use XDG base directory on Linux and macOS (#527) +#### Upgrade guide + +* **On Linux or macOS** Please move your Dnote files to new directories based on the XDG base directory specfication. **On Windows**, no action is required. + +``` +# Move the database file +mv ~/.dnote/dnote.db ~/.local/share/dnote/dnote.db + +# Move the config file +mv ~/.dnote/dnoterc ~/.config/dnote/dnoterc + +# Delete ~/.dnote. (it is safe to delete DNOTE_TMPCONTENT.md files, if they exist.) +rm -rf ~/.dnote +``` + +If `~/.dnote` directory exists, dnote will continue to use that directory for backward compatibility until the next major release. + +#### Added + - Add `--content-only` flag to print the note content only (#528) +#### Changed + +- Use XDG base directory on Linux and macOS (#527) + ### 0.11.1 - 2020-04-25 #### Fixed From 504ba4485210964cb619d86f69726b70f50e4ce5 Mon Sep 17 00:00:00 2001 From: Sung Won Cho Date: Wed, 8 Dec 2021 22:26:15 +1100 Subject: [PATCH 07/91] Add 2021 (#574) --- browser/gulpfile.js | 2 +- browser/src/browser.d.ts | 2 +- browser/src/global.d.ts | 2 +- browser/src/scripts/components/App.tsx | 2 +- browser/src/scripts/components/BookIcon.tsx | 2 +- .../src/scripts/components/BookSelector.tsx | 2 +- browser/src/scripts/components/CloseIcon.tsx | 2 +- browser/src/scripts/components/Composer.tsx | 2 +- browser/src/scripts/components/Flash.tsx | 2 +- browser/src/scripts/components/Header.tsx | 2 +- browser/src/scripts/components/Home.tsx | 2 +- browser/src/scripts/components/Link.tsx | 2 +- browser/src/scripts/components/Menu.tsx | 2 +- .../src/scripts/components/MenuToggleIcon.tsx | 2 +- browser/src/scripts/components/Settings.tsx | 2 +- browser/src/scripts/components/Success.tsx | 2 +- browser/src/scripts/popup.tsx | 2 +- browser/src/scripts/store/auth/actions.ts | 2 +- browser/src/scripts/store/auth/reducers.ts | 2 +- browser/src/scripts/store/auth/types.ts | 2 +- browser/src/scripts/store/books/actions.ts | 2 +- browser/src/scripts/store/books/reducers.ts | 2 +- browser/src/scripts/store/books/types.ts | 2 +- browser/src/scripts/store/composer/actions.ts | 2 +- browser/src/scripts/store/composer/reducers.ts | 2 +- browser/src/scripts/store/composer/types.ts | 2 +- browser/src/scripts/store/hooks.ts | 2 +- browser/src/scripts/store/index.ts | 2 +- browser/src/scripts/store/location/actions.ts | 2 +- browser/src/scripts/store/location/reducers.ts | 2 +- browser/src/scripts/store/location/types.ts | 2 +- browser/src/scripts/store/settings/actions.ts | 2 +- browser/src/scripts/store/settings/reducers.ts | 2 +- browser/src/scripts/store/settings/types.ts | 2 +- browser/src/scripts/store/types.ts | 2 +- browser/src/scripts/utils/config.ts | 2 +- browser/src/scripts/utils/ext.ts | 2 +- browser/src/scripts/utils/fetch.js | 2 +- browser/src/scripts/utils/services.ts | 2 +- browser/src/scripts/utils/storage.ts | 2 +- browser/src/styles/popup.css | 2 +- browser/src/styles/select.css | 2 +- browser/webpack.config.js | 2 +- jslib/src/helpers/arr.spec.ts | 2 +- jslib/src/helpers/arr.ts | 2 +- jslib/src/helpers/books.spec.ts | 2 +- jslib/src/helpers/books.ts | 2 +- jslib/src/helpers/filters.ts | 2 +- jslib/src/helpers/http.ts | 2 +- jslib/src/helpers/index.ts | 2 +- jslib/src/helpers/keyboard.ts | 2 +- jslib/src/helpers/obj.ts | 2 +- jslib/src/helpers/perf.ts | 2 +- jslib/src/helpers/queries.ts | 2 +- jslib/src/helpers/search.spec.ts | 2 +- jslib/src/helpers/search.ts | 2 +- jslib/src/helpers/select.ts | 2 +- jslib/src/helpers/url.ts | 2 +- jslib/src/index.ts | 2 +- jslib/src/operations/books.ts | 2 +- jslib/src/operations/docs.ts | 2 +- jslib/src/operations/index.ts | 2 +- jslib/src/operations/notes.ts | 2 +- jslib/src/operations/types.ts | 2 +- jslib/src/services/books.ts | 2 +- jslib/src/services/index.ts | 2 +- jslib/src/services/notes.ts | 2 +- jslib/src/services/types.ts | 2 +- jslib/src/services/users.ts | 2 +- pkg/assert/assert.go | 2 +- pkg/cli/client/client.go | 2 +- pkg/cli/client/client_test.go | 18 ++++++++++++++++++ pkg/cli/cmd/add/add.go | 2 +- pkg/cli/cmd/cat/cat.go | 2 +- pkg/cli/cmd/edit/book.go | 2 +- pkg/cli/cmd/edit/edit.go | 2 +- pkg/cli/cmd/edit/note.go | 2 +- pkg/cli/cmd/find/find.go | 2 +- pkg/cli/cmd/find/lexer.go | 2 +- pkg/cli/cmd/find/lexer_test.go | 2 +- pkg/cli/cmd/login/login.go | 2 +- pkg/cli/cmd/login/login_test.go | 18 ++++++++++++++++++ pkg/cli/cmd/logout/logout.go | 2 +- pkg/cli/cmd/ls/ls.go | 2 +- pkg/cli/cmd/remove/remove.go | 2 +- pkg/cli/cmd/root/root.go | 2 +- pkg/cli/cmd/sync/main_test.go | 18 ++++++++++++++++++ pkg/cli/cmd/sync/merge.go | 2 +- pkg/cli/cmd/sync/merge_test.go | 2 +- pkg/cli/cmd/sync/sync.go | 2 +- pkg/cli/cmd/sync/sync_test.go | 2 +- pkg/cli/cmd/version/version.go | 2 +- pkg/cli/cmd/view/view.go | 2 +- pkg/cli/config/config.go | 2 +- pkg/cli/consts/consts.go | 2 +- pkg/cli/context/ctx.go | 2 +- pkg/cli/context/testutils.go | 2 +- pkg/cli/crypt/crypto.go | 2 +- pkg/cli/crypt/crypto_test.go | 2 +- pkg/cli/database/models.go | 2 +- pkg/cli/database/models_test.go | 2 +- pkg/cli/database/queries.go | 2 +- pkg/cli/database/queries_test.go | 2 +- pkg/cli/database/sql.go | 2 +- pkg/cli/database/testutils.go | 2 +- pkg/cli/dirs/dirs.go | 18 ++++++++++++++++++ pkg/cli/dirs/dirs_test.go | 18 ++++++++++++++++++ pkg/cli/dirs/dirs_unix.go | 18 ++++++++++++++++++ pkg/cli/dirs/dirs_unix_test.go | 18 ++++++++++++++++++ pkg/cli/dirs/dirs_windows.go | 18 ++++++++++++++++++ pkg/cli/dirs/dirs_windows_test.go | 18 ++++++++++++++++++ pkg/cli/infra/init.go | 2 +- pkg/cli/infra/init_test.go | 2 +- pkg/cli/log/log.go | 2 +- pkg/cli/main.go | 2 +- pkg/cli/main_test.go | 2 +- pkg/cli/migrate/legacy.go | 2 +- pkg/cli/migrate/legacy_test.go | 2 +- pkg/cli/migrate/migrate.go | 2 +- pkg/cli/migrate/migrate_test.go | 2 +- pkg/cli/migrate/migrations.go | 2 +- pkg/cli/output/output.go | 2 +- pkg/cli/testutils/main.go | 2 +- pkg/cli/testutils/setup.go | 2 +- pkg/cli/ui/editor.go | 2 +- pkg/cli/ui/editor_test.go | 2 +- pkg/cli/ui/terminal.go | 2 +- pkg/cli/upgrade/upgrade.go | 2 +- pkg/cli/upgrade/upgrade_test.go | 2 +- pkg/cli/utils/diff/diff.go | 2 +- pkg/cli/utils/diff/diff_test.go | 2 +- pkg/cli/utils/files.go | 2 +- pkg/cli/utils/utils.go | 2 +- pkg/cli/validate/book_test.go | 2 +- pkg/cli/validate/books.go | 2 +- pkg/clock/clock.go | 2 +- pkg/server/api/auth.go | 2 +- pkg/server/api/auth_test.go | 2 +- pkg/server/api/health.go | 2 +- pkg/server/api/health_test.go | 2 +- pkg/server/api/helpers.go | 2 +- pkg/server/api/main_test.go | 2 +- pkg/server/api/notes.go | 2 +- pkg/server/api/notes_test.go | 2 +- pkg/server/api/routes.go | 2 +- pkg/server/api/routes_test.go | 2 +- pkg/server/api/testutils.go | 2 +- pkg/server/api/user.go | 2 +- pkg/server/api/user_test.go | 2 +- pkg/server/api/v3_auth.go | 2 +- pkg/server/api/v3_auth_test.go | 2 +- pkg/server/api/v3_books.go | 2 +- pkg/server/api/v3_books_test.go | 2 +- pkg/server/api/v3_notes.go | 2 +- pkg/server/api/v3_notes_test.go | 2 +- pkg/server/api/v3_sync.go | 2 +- pkg/server/api/v3_sync_test.go | 2 +- pkg/server/app/app.go | 2 +- pkg/server/app/books.go | 2 +- pkg/server/app/books_test.go | 2 +- pkg/server/app/doc.go | 2 +- pkg/server/app/email.go | 2 +- pkg/server/app/email_test.go | 2 +- pkg/server/app/helpers.go | 2 +- pkg/server/app/helpers_test.go | 2 +- pkg/server/app/main_test.go | 2 +- pkg/server/app/notes.go | 2 +- pkg/server/app/notes_test.go | 2 +- pkg/server/app/sessions.go | 2 +- pkg/server/app/testutils.go | 2 +- pkg/server/app/users.go | 2 +- pkg/server/app/users_test.go | 2 +- pkg/server/config/config.go | 18 ++++++++++++++++++ pkg/server/config/config_test.go | 2 +- pkg/server/crypt/crypt.go | 2 +- pkg/server/database/consts.go | 2 +- pkg/server/database/database.go | 2 +- pkg/server/database/migrate.go | 2 +- pkg/server/database/migrate/main.go | 2 +- pkg/server/database/models.go | 2 +- pkg/server/database/notes.go | 2 +- pkg/server/database/types.go | 2 +- pkg/server/handlers/auth.go | 18 ++++++++++++++++++ pkg/server/handlers/helpers.go | 18 ++++++++++++++++++ pkg/server/handlers/helpers_test.go | 2 +- pkg/server/handlers/limit.go | 2 +- pkg/server/handlers/logging.go | 18 ++++++++++++++++++ pkg/server/handlers/main_test.go | 2 +- pkg/server/helpers/const.go | 2 +- pkg/server/helpers/helpers.go | 2 +- pkg/server/job/job.go | 2 +- pkg/server/job/job_test.go | 2 +- pkg/server/job/remind/inactive.go | 2 +- pkg/server/job/remind/inactive_test.go | 2 +- pkg/server/job/remind/main_test.go | 2 +- pkg/server/log/log.go | 2 +- pkg/server/mailer/backend.go | 2 +- pkg/server/mailer/mailer.go | 2 +- pkg/server/mailer/mailer_test.go | 2 +- pkg/server/mailer/templates/main.go | 2 +- pkg/server/mailer/tokens.go | 2 +- pkg/server/mailer/types.go | 2 +- pkg/server/main.go | 2 +- pkg/server/operations/doc.go | 2 +- pkg/server/operations/main_test.go | 2 +- pkg/server/operations/notes.go | 2 +- pkg/server/operations/notes_test.go | 2 +- pkg/server/permissions/permissions.go | 2 +- pkg/server/permissions/permissions_test.go | 2 +- pkg/server/presenters/book.go | 2 +- pkg/server/presenters/email_preference.go | 2 +- pkg/server/presenters/helpers.go | 2 +- pkg/server/presenters/note.go | 2 +- pkg/server/session/session.go | 18 ++++++++++++++++++ pkg/server/session/session_test.go | 2 +- pkg/server/testutils/main.go | 2 +- pkg/server/tmpl/app.go | 2 +- pkg/server/tmpl/app_test.go | 2 +- pkg/server/tmpl/data.go | 2 +- pkg/server/tmpl/data_test.go | 2 +- pkg/server/tmpl/main_test.go | 2 +- pkg/server/tmpl/tmpl.go | 2 +- pkg/server/token/main_test.go | 2 +- pkg/server/token/token.go | 2 +- pkg/server/token/token_test.go | 2 +- pkg/server/web/handlers.go | 2 +- pkg/server/web/handlers_test.go | 2 +- pkg/server/web/main_test.go | 2 +- pkg/watcher/main.go | 2 +- scripts/license.sh | 4 ++-- web/assets/service-worker.js | 2 +- web/declrations.d.ts | 2 +- web/jest.config.js | 2 +- web/src/client.tsx | 2 +- web/src/components/App/App.global.scss | 2 +- web/src/components/App/App.scss | 2 +- web/src/components/App/HeaderData.tsx | 2 +- web/src/components/App/_bootstrap.scss | 2 +- web/src/components/App/_buttons.scss | 2 +- web/src/components/App/_font.scss | 2 +- web/src/components/App/_grid.scss | 2 +- web/src/components/App/_hljs.scss | 2 +- web/src/components/App/_markdown.scss | 2 +- web/src/components/App/_marker.scss | 2 +- web/src/components/App/_reboot.scss | 2 +- web/src/components/App/_rem.scss | 2 +- web/src/components/App/_responsive.scss | 2 +- web/src/components/App/_select.scss | 2 +- web/src/components/App/_shared.scss | 2 +- web/src/components/App/_theme.scss | 2 +- web/src/components/App/_variables.scss | 2 +- web/src/components/App/index.tsx | 2 +- web/src/components/Books/BookHolder.scss | 2 +- web/src/components/Books/BookHolder.tsx | 2 +- web/src/components/Books/BookItem/Actions.scss | 2 +- web/src/components/Books/BookItem/Actions.tsx | 2 +- .../components/Books/BookItem/BookItem.scss | 2 +- web/src/components/Books/BookItem/index.tsx | 2 +- web/src/components/Books/BookList.scss | 2 +- web/src/components/Books/BookList.tsx | 2 +- web/src/components/Books/Books.scss | 2 +- web/src/components/Books/Content.scss | 2 +- web/src/components/Books/Content.tsx | 2 +- web/src/components/Books/CreateBookButton.scss | 2 +- web/src/components/Books/CreateBookButton.tsx | 2 +- web/src/components/Books/CreateBookModal.scss | 2 +- web/src/components/Books/CreateBookModal.tsx | 2 +- web/src/components/Books/DeleteBookModal.scss | 2 +- web/src/components/Books/DeleteBookModal.tsx | 2 +- web/src/components/Books/EmptyList.scss | 2 +- web/src/components/Books/EmptyList.tsx | 2 +- web/src/components/Books/HeadData.tsx | 2 +- web/src/components/Books/index.tsx | 2 +- web/src/components/Common/Auth.scss | 2 +- web/src/components/Common/Button/Button.scss | 2 +- web/src/components/Common/Button/index.tsx | 2 +- .../Common/Editor/BookSelector/OptionItem.scss | 2 +- .../Common/Editor/BookSelector/OptionItem.tsx | 2 +- .../Common/Editor/BookSelector/index.scss | 2 +- .../Common/Editor/BookSelector/index.tsx | 2 +- web/src/components/Common/Editor/Editor.scss | 2 +- web/src/components/Common/Editor/Preview.scss | 2 +- web/src/components/Common/Editor/Preview.tsx | 2 +- web/src/components/Common/Editor/Textarea.scss | 2 +- web/src/components/Common/Editor/Textarea.tsx | 2 +- web/src/components/Common/Editor/index.tsx | 2 +- web/src/components/Common/Flash/Flash.scss | 2 +- web/src/components/Common/Flash/index.tsx | 2 +- .../Common/ItemActions/ItemActions.scss | 2 +- .../components/Common/ItemActions/index.tsx | 2 +- web/src/components/Common/LegacyFooter.js | 2 +- web/src/components/Common/Menu/Menu.scss | 2 +- web/src/components/Common/Menu/index.tsx | 2 +- web/src/components/Common/Menu/types.ts | 2 +- web/src/components/Common/MobileMenu.scss | 2 +- web/src/components/Common/MobileMenu.tsx | 2 +- web/src/components/Common/Modal/Body.tsx | 2 +- web/src/components/Common/Modal/Header.tsx | 2 +- web/src/components/Common/Modal/Modal.scss | 2 +- web/src/components/Common/Modal/ModalBody.scss | 2 +- .../components/Common/Modal/ModalHeader.scss | 2 +- web/src/components/Common/Modal/index.tsx | 2 +- web/src/components/Common/MultiSelect.scss | 2 +- web/src/components/Common/MultiSelect.tsx | 2 +- web/src/components/Common/NotFound.tsx | 2 +- web/src/components/Common/Note/Content.tsx | 2 +- web/src/components/Common/Note/Footer.tsx | 2 +- web/src/components/Common/Note/Note.scss | 2 +- .../components/Common/Note/Placeholder.scss | 2 +- web/src/components/Common/Note/Placeholder.tsx | 2 +- web/src/components/Common/Note/index.tsx | 2 +- .../Common/PageToolbar/Paginator/PageLink.tsx | 2 +- .../PageToolbar/Paginator/Paginator.scss | 2 +- .../Common/PageToolbar/Paginator/index.tsx | 2 +- .../Common/PageToolbar/SelectMenu.scss | 2 +- .../Common/PageToolbar/SelectMenu.tsx | 2 +- .../components/Common/PageToolbar/index.scss | 2 +- .../components/Common/PageToolbar/index.tsx | 2 +- web/src/components/Common/PayWall.scss | 2 +- web/src/components/Common/PayWall.tsx | 2 +- web/src/components/Common/Popover/Popover.scss | 2 +- .../Common/Popover/PopoverContent.scss | 2 +- .../Common/Popover/PopoverContent.tsx | 2 +- web/src/components/Common/Popover/index.tsx | 2 +- web/src/components/Common/Popover/types.ts | 2 +- .../components/Common/SearchInput/Actions.tsx | 2 +- .../Common/SearchInput/SearchInput.scss | 2 +- .../components/Common/SearchInput/index.tsx | 2 +- .../components/Common/SearchableMenu/Item.tsx | 2 +- .../Common/SearchableMenu/Result.tsx | 2 +- .../Common/SearchableMenu/SearchableMenu.scss | 2 +- .../components/Common/SearchableMenu/index.tsx | 2 +- .../SettingsSidebar.module.scss | 2 +- .../Common/Sidebar/SettingsSidebar/index.js | 2 +- .../Common/Sidebar/Sidebar.module.scss | 2 +- web/src/components/Common/SidebarToggle.js | 2 +- .../Common/SidebarToggle.module.scss | 2 +- web/src/components/Common/SystemMessage.scss | 2 +- web/src/components/Common/SystemMessage.tsx | 2 +- web/src/components/Common/Time.scss | 2 +- web/src/components/Common/Time.tsx | 2 +- web/src/components/Common/Toggle.scss | 2 +- web/src/components/Common/Toggle.tsx | 2 +- web/src/components/Common/Tooltip/Overlay.tsx | 2 +- web/src/components/Common/Tooltip/Tooltip.scss | 2 +- web/src/components/Common/Tooltip/index.tsx | 2 +- web/src/components/Edit/Content.tsx | 2 +- web/src/components/Edit/index.tsx | 2 +- .../EmailPreference/EmailPreference.scss | 2 +- web/src/components/EmailPreference/index.tsx | 2 +- web/src/components/Header/AccountMenu.scss | 2 +- web/src/components/Header/AccountMenu.tsx | 2 +- web/src/components/Header/DemoHeader.js | 2 +- .../components/Header/DemoHeader.module.scss | 2 +- web/src/components/Header/Nav/Item.scss | 2 +- web/src/components/Header/Nav/Item.tsx | 2 +- web/src/components/Header/Nav/Nav.scss | 2 +- web/src/components/Header/Nav/index.tsx | 2 +- web/src/components/Header/Normal.scss | 2 +- web/src/components/Header/Normal.tsx | 2 +- web/src/components/Header/Note/Guest.scss | 2 +- web/src/components/Header/Note/Guest.tsx | 2 +- .../components/Header/Note/Placeholder.scss | 2 +- web/src/components/Header/Note/Placeholder.tsx | 2 +- web/src/components/Header/Note/index.scss | 2 +- web/src/components/Header/Note/index.tsx | 2 +- .../SearchBar/AdvancedPanel/AdvancedPanel.scss | 2 +- .../SearchBar/AdvancedPanel/BookSearch.tsx | 2 +- .../SearchBar/AdvancedPanel/WordsSearch.tsx | 2 +- .../Header/SearchBar/AdvancedPanel/index.tsx | 2 +- .../components/Header/SearchBar/SearchBar.scss | 2 +- web/src/components/Header/SearchBar/index.tsx | 2 +- web/src/components/Home/HeadData.tsx | 2 +- web/src/components/Home/Home.scss | 2 +- web/src/components/Home/NoteGroup/Header.scss | 2 +- web/src/components/Home/NoteGroup/Header.tsx | 2 +- web/src/components/Home/NoteGroup/List.scss | 2 +- web/src/components/Home/NoteGroup/List.tsx | 2 +- .../components/Home/NoteGroup/NoteGroup.scss | 2 +- .../components/Home/NoteGroup/NoteItem.scss | 2 +- web/src/components/Home/NoteGroup/NoteItem.tsx | 2 +- .../components/Home/NoteGroup/Placeholder.scss | 2 +- .../components/Home/NoteGroup/Placeholder.tsx | 2 +- web/src/components/Home/NoteGroup/index.tsx | 2 +- web/src/components/Home/index.tsx | 2 +- web/src/components/Icons/Arrow.js | 2 +- web/src/components/Icons/Atom.js | 2 +- web/src/components/Icons/Book.tsx | 2 +- web/src/components/Icons/BookPlus.tsx | 2 +- web/src/components/Icons/Books.js | 2 +- web/src/components/Icons/Box.tsx | 2 +- web/src/components/Icons/Caret.tsx | 2 +- web/src/components/Icons/CaretSolid.tsx | 2 +- web/src/components/Icons/Check.js | 2 +- web/src/components/Icons/CheckCircle.tsx | 2 +- web/src/components/Icons/Chrome.js | 2 +- web/src/components/Icons/Close.tsx | 2 +- web/src/components/Icons/Cloud.js | 2 +- web/src/components/Icons/CreditCard.js | 2 +- web/src/components/Icons/Dashboard.tsx | 2 +- web/src/components/Icons/Dots.tsx | 2 +- web/src/components/Icons/Email.js | 2 +- web/src/components/Icons/Firefox.js | 2 +- web/src/components/Icons/Globe.tsx | 2 +- web/src/components/Icons/Home.tsx | 2 +- web/src/components/Icons/Link.js | 2 +- web/src/components/Icons/Loading.js | 2 +- web/src/components/Icons/Lock.js | 2 +- web/src/components/Icons/Logo.tsx | 2 +- web/src/components/Icons/LogoWithText.tsx | 2 +- web/src/components/Icons/Menu.js | 2 +- web/src/components/Icons/Note.tsx | 2 +- web/src/components/Icons/Search.tsx | 2 +- web/src/components/Icons/Server.tsx | 2 +- web/src/components/Icons/Spinner.js | 2 +- web/src/components/Icons/Terminal.js | 2 +- web/src/components/Icons/Trash.tsx | 2 +- web/src/components/Icons/Unlink.js | 2 +- web/src/components/Icons/User.tsx | 2 +- web/src/components/Icons/Users.js | 2 +- web/src/components/Icons/Web.js | 2 +- web/src/components/Icons/types.ts | 2 +- web/src/components/Join/JoinForm.tsx | 2 +- web/src/components/Join/index.tsx | 2 +- web/src/components/Login/LoginForm.tsx | 2 +- web/src/components/Login/index.tsx | 2 +- web/src/components/New/Content.tsx | 2 +- web/src/components/New/New.scss | 2 +- web/src/components/New/index.tsx | 2 +- web/src/components/Note/DeleteModal.scss | 2 +- web/src/components/Note/DeleteModal.tsx | 2 +- web/src/components/Note/FooterActions.scss | 2 +- web/src/components/Note/FooterActions.tsx | 2 +- web/src/components/Note/Header.scss | 2 +- web/src/components/Note/Header.tsx | 2 +- web/src/components/Note/HeaderData.tsx | 2 +- web/src/components/Note/HeaderRight.tsx | 2 +- .../components/Note/ShareModal/CopyButton.tsx | 2 +- .../components/Note/ShareModal/ShareModal.scss | 2 +- web/src/components/Note/ShareModal/index.tsx | 2 +- web/src/components/Note/index.scss | 2 +- web/src/components/Note/index.tsx | 2 +- .../components/PasswordReset/Confirm/Form.tsx | 2 +- .../components/PasswordReset/Confirm/index.tsx | 2 +- .../components/PasswordReset/Request/Form.scss | 2 +- .../components/PasswordReset/Request/Form.tsx | 2 +- .../PasswordReset/Request/Request.scss | 2 +- .../components/PasswordReset/Request/index.tsx | 2 +- web/src/components/Settings/About/index.tsx | 2 +- .../components/Settings/Account/EmailModal.tsx | 2 +- .../Settings/Account/EmailVerificationRow.tsx | 2 +- .../Settings/Account/PasswordModal.tsx | 2 +- web/src/components/Settings/Account/index.tsx | 2 +- .../Settings/Notifications/Form.scss | 2 +- .../components/Settings/Notifications/Form.tsx | 2 +- .../Settings/Notifications/Notifications.scss | 2 +- .../Settings/Notifications/index.tsx | 2 +- web/src/components/Settings/SettingRow.scss | 2 +- web/src/components/Settings/SettingRow.tsx | 2 +- web/src/components/Settings/Settings.scss | 2 +- web/src/components/Settings/Sidebar.scss | 2 +- web/src/components/Settings/Sidebar.tsx | 2 +- web/src/components/Settings/index.tsx | 2 +- web/src/components/Splash/Splash.module.scss | 2 +- web/src/components/Splash/index.tsx | 2 +- web/src/components/TabBar/Item.scss | 2 +- web/src/components/TabBar/Item.tsx | 2 +- web/src/components/TabBar/TabBar.scss | 2 +- web/src/components/TabBar/index.tsx | 2 +- web/src/components/VerifyEmail/index.tsx | 2 +- web/src/helpers/accessibility.ts | 2 +- web/src/helpers/browser.js | 2 +- web/src/helpers/error.js | 2 +- web/src/helpers/markdown.ts | 2 +- web/src/helpers/time/format.spec.ts | 2 +- web/src/helpers/time/format.ts | 2 +- web/src/helpers/time/index.spec.ts | 2 +- web/src/helpers/time/index.ts | 2 +- web/src/helpers/user.js | 2 +- web/src/hocs/guestOnly.tsx | 2 +- web/src/hocs/scrollTop.tsx | 2 +- web/src/hocs/userOnly.tsx | 2 +- web/src/libs/config.ts | 2 +- web/src/libs/countries.js | 2 +- web/src/libs/dom.ts | 2 +- web/src/libs/editor.spec.ts | 2 +- web/src/libs/editor.ts | 2 +- web/src/libs/encoding.js | 2 +- web/src/libs/encoding_test.js | 2 +- web/src/libs/fts/lexer.spec.ts | 2 +- web/src/libs/fts/lexer.ts | 2 +- web/src/libs/hooks/dom.ts | 2 +- web/src/libs/hooks/editor.ts | 2 +- web/src/libs/hooks/index.ts | 2 +- web/src/libs/localStorage.ts | 2 +- web/src/libs/notes.ts | 2 +- web/src/libs/operations.ts | 2 +- web/src/libs/paths.spec.ts | 2 +- web/src/libs/paths.ts | 2 +- web/src/libs/restoreScroll.js | 2 +- web/src/libs/scopeTab.js | 2 +- web/src/libs/search.ts | 2 +- web/src/libs/services.ts | 2 +- web/src/libs/string.ts | 2 +- web/src/libs/subscription.js | 2 +- web/src/libs/ui.js | 2 +- web/src/routes.tsx | 2 +- web/src/store/auth/actions.ts | 2 +- web/src/store/auth/index.ts | 2 +- web/src/store/auth/reducers.ts | 2 +- web/src/store/auth/type.ts | 2 +- web/src/store/books/actions.ts | 2 +- web/src/store/books/index.ts | 2 +- web/src/store/books/reducers.ts | 2 +- web/src/store/books/type.ts | 2 +- web/src/store/editor/actions.ts | 2 +- web/src/store/editor/index.ts | 2 +- web/src/store/editor/reducers.ts | 2 +- web/src/store/editor/type.ts | 2 +- web/src/store/filters/actions.ts | 2 +- web/src/store/filters/index.ts | 2 +- web/src/store/filters/reducers.ts | 2 +- web/src/store/filters/type.ts | 2 +- web/src/store/form/actions.ts | 2 +- web/src/store/form/index.ts | 2 +- web/src/store/form/reducers.ts | 2 +- web/src/store/form/type.ts | 2 +- web/src/store/hooks.ts | 2 +- web/src/store/index.ts | 2 +- web/src/store/note/actions.ts | 2 +- web/src/store/note/index.ts | 2 +- web/src/store/note/reducers.ts | 2 +- web/src/store/note/type.ts | 2 +- web/src/store/notes/actions.ts | 2 +- web/src/store/notes/index.ts | 2 +- web/src/store/notes/reducers.ts | 2 +- web/src/store/notes/type.ts | 2 +- web/src/store/route/actions.ts | 2 +- web/src/store/route/index.ts | 2 +- web/src/store/route/reducers.ts | 2 +- web/src/store/route/type.ts | 2 +- web/src/store/types.ts | 2 +- web/src/store/ui/actions.ts | 2 +- web/src/store/ui/index.ts | 2 +- web/src/store/ui/reducers.ts | 2 +- web/src/store/ui/type.ts | 2 +- web/webpack/dev.config.js | 2 +- web/webpack/externals.js | 2 +- web/webpack/paths.js | 2 +- web/webpack/plugins.js | 2 +- web/webpack/prod.config.js | 2 +- web/webpack/resolve.js | 2 +- web/webpack/rules/css.js | 2 +- web/webpack/rules/image.js | 2 +- web/webpack/rules/index.js | 2 +- web/webpack/rules/javascript.js | 2 +- 556 files changed, 795 insertions(+), 543 deletions(-) diff --git a/browser/gulpfile.js b/browser/gulpfile.js index ae3d262e..410c686f 100644 --- a/browser/gulpfile.js +++ b/browser/gulpfile.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/browser/src/browser.d.ts b/browser/src/browser.d.ts index 415ffff0..976b9536 100644 --- a/browser/src/browser.d.ts +++ b/browser/src/browser.d.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/browser/src/global.d.ts b/browser/src/global.d.ts index 0bf0f41c..1d7d6f17 100644 --- a/browser/src/global.d.ts +++ b/browser/src/global.d.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/browser/src/scripts/components/App.tsx b/browser/src/scripts/components/App.tsx index 19407637..2671132d 100644 --- a/browser/src/scripts/components/App.tsx +++ b/browser/src/scripts/components/App.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/browser/src/scripts/components/BookIcon.tsx b/browser/src/scripts/components/BookIcon.tsx index 9f9acb91..439658e7 100644 --- a/browser/src/scripts/components/BookIcon.tsx +++ b/browser/src/scripts/components/BookIcon.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/browser/src/scripts/components/BookSelector.tsx b/browser/src/scripts/components/BookSelector.tsx index 4ab86d87..cbfa3e5a 100644 --- a/browser/src/scripts/components/BookSelector.tsx +++ b/browser/src/scripts/components/BookSelector.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/browser/src/scripts/components/CloseIcon.tsx b/browser/src/scripts/components/CloseIcon.tsx index c574d1ce..1da66176 100644 --- a/browser/src/scripts/components/CloseIcon.tsx +++ b/browser/src/scripts/components/CloseIcon.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/browser/src/scripts/components/Composer.tsx b/browser/src/scripts/components/Composer.tsx index 154d3c03..f5755ebb 100644 --- a/browser/src/scripts/components/Composer.tsx +++ b/browser/src/scripts/components/Composer.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/browser/src/scripts/components/Flash.tsx b/browser/src/scripts/components/Flash.tsx index aac61e1d..ed76823e 100644 --- a/browser/src/scripts/components/Flash.tsx +++ b/browser/src/scripts/components/Flash.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/browser/src/scripts/components/Header.tsx b/browser/src/scripts/components/Header.tsx index 1ebf8876..ac8c0a9f 100644 --- a/browser/src/scripts/components/Header.tsx +++ b/browser/src/scripts/components/Header.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/browser/src/scripts/components/Home.tsx b/browser/src/scripts/components/Home.tsx index ff0b8ec4..694503a5 100644 --- a/browser/src/scripts/components/Home.tsx +++ b/browser/src/scripts/components/Home.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/browser/src/scripts/components/Link.tsx b/browser/src/scripts/components/Link.tsx index 11eb6927..5ac43614 100644 --- a/browser/src/scripts/components/Link.tsx +++ b/browser/src/scripts/components/Link.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/browser/src/scripts/components/Menu.tsx b/browser/src/scripts/components/Menu.tsx index a6cbfa9f..222e08a8 100644 --- a/browser/src/scripts/components/Menu.tsx +++ b/browser/src/scripts/components/Menu.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/browser/src/scripts/components/MenuToggleIcon.tsx b/browser/src/scripts/components/MenuToggleIcon.tsx index 0ff072fb..ba0c0747 100644 --- a/browser/src/scripts/components/MenuToggleIcon.tsx +++ b/browser/src/scripts/components/MenuToggleIcon.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/browser/src/scripts/components/Settings.tsx b/browser/src/scripts/components/Settings.tsx index f9465e0c..f7f210c8 100644 --- a/browser/src/scripts/components/Settings.tsx +++ b/browser/src/scripts/components/Settings.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/browser/src/scripts/components/Success.tsx b/browser/src/scripts/components/Success.tsx index 5a198d5a..aff0847c 100644 --- a/browser/src/scripts/components/Success.tsx +++ b/browser/src/scripts/components/Success.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/browser/src/scripts/popup.tsx b/browser/src/scripts/popup.tsx index a0f3aef9..482a5508 100644 --- a/browser/src/scripts/popup.tsx +++ b/browser/src/scripts/popup.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/browser/src/scripts/store/auth/actions.ts b/browser/src/scripts/store/auth/actions.ts index 793d1ef6..43065635 100644 --- a/browser/src/scripts/store/auth/actions.ts +++ b/browser/src/scripts/store/auth/actions.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/browser/src/scripts/store/auth/reducers.ts b/browser/src/scripts/store/auth/reducers.ts index 50c39e12..0cd5cff8 100644 --- a/browser/src/scripts/store/auth/reducers.ts +++ b/browser/src/scripts/store/auth/reducers.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/browser/src/scripts/store/auth/types.ts b/browser/src/scripts/store/auth/types.ts index c3a08d42..57ea2675 100644 --- a/browser/src/scripts/store/auth/types.ts +++ b/browser/src/scripts/store/auth/types.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/browser/src/scripts/store/books/actions.ts b/browser/src/scripts/store/books/actions.ts index 39c50c14..d1583f8a 100644 --- a/browser/src/scripts/store/books/actions.ts +++ b/browser/src/scripts/store/books/actions.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/browser/src/scripts/store/books/reducers.ts b/browser/src/scripts/store/books/reducers.ts index 7ea1cb12..424382bd 100644 --- a/browser/src/scripts/store/books/reducers.ts +++ b/browser/src/scripts/store/books/reducers.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/browser/src/scripts/store/books/types.ts b/browser/src/scripts/store/books/types.ts index cf060f0c..1d0c4ff6 100644 --- a/browser/src/scripts/store/books/types.ts +++ b/browser/src/scripts/store/books/types.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/browser/src/scripts/store/composer/actions.ts b/browser/src/scripts/store/composer/actions.ts index f77c3ba0..d182c49b 100644 --- a/browser/src/scripts/store/composer/actions.ts +++ b/browser/src/scripts/store/composer/actions.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/browser/src/scripts/store/composer/reducers.ts b/browser/src/scripts/store/composer/reducers.ts index 4a09017e..5a85645b 100644 --- a/browser/src/scripts/store/composer/reducers.ts +++ b/browser/src/scripts/store/composer/reducers.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/browser/src/scripts/store/composer/types.ts b/browser/src/scripts/store/composer/types.ts index c6e003c0..55eedd06 100644 --- a/browser/src/scripts/store/composer/types.ts +++ b/browser/src/scripts/store/composer/types.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/browser/src/scripts/store/hooks.ts b/browser/src/scripts/store/hooks.ts index 01b5c6cc..445af623 100644 --- a/browser/src/scripts/store/hooks.ts +++ b/browser/src/scripts/store/hooks.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/browser/src/scripts/store/index.ts b/browser/src/scripts/store/index.ts index 902a1038..d4b339a2 100644 --- a/browser/src/scripts/store/index.ts +++ b/browser/src/scripts/store/index.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/browser/src/scripts/store/location/actions.ts b/browser/src/scripts/store/location/actions.ts index 0cf6f1fd..3800d66a 100644 --- a/browser/src/scripts/store/location/actions.ts +++ b/browser/src/scripts/store/location/actions.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/browser/src/scripts/store/location/reducers.ts b/browser/src/scripts/store/location/reducers.ts index b1e155c1..5e5f586f 100644 --- a/browser/src/scripts/store/location/reducers.ts +++ b/browser/src/scripts/store/location/reducers.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/browser/src/scripts/store/location/types.ts b/browser/src/scripts/store/location/types.ts index 84a085e9..a584c0dc 100644 --- a/browser/src/scripts/store/location/types.ts +++ b/browser/src/scripts/store/location/types.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/browser/src/scripts/store/settings/actions.ts b/browser/src/scripts/store/settings/actions.ts index a44adad4..5c01b9c4 100644 --- a/browser/src/scripts/store/settings/actions.ts +++ b/browser/src/scripts/store/settings/actions.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/browser/src/scripts/store/settings/reducers.ts b/browser/src/scripts/store/settings/reducers.ts index 438a6236..e3b0078a 100644 --- a/browser/src/scripts/store/settings/reducers.ts +++ b/browser/src/scripts/store/settings/reducers.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/browser/src/scripts/store/settings/types.ts b/browser/src/scripts/store/settings/types.ts index 772b256a..6e8fba54 100644 --- a/browser/src/scripts/store/settings/types.ts +++ b/browser/src/scripts/store/settings/types.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/browser/src/scripts/store/types.ts b/browser/src/scripts/store/types.ts index a879febc..2461b550 100644 --- a/browser/src/scripts/store/types.ts +++ b/browser/src/scripts/store/types.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/browser/src/scripts/utils/config.ts b/browser/src/scripts/utils/config.ts index 435539e1..eca1f6aa 100644 --- a/browser/src/scripts/utils/config.ts +++ b/browser/src/scripts/utils/config.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/browser/src/scripts/utils/ext.ts b/browser/src/scripts/utils/ext.ts index 5b8e6bfb..95be7bf6 100644 --- a/browser/src/scripts/utils/ext.ts +++ b/browser/src/scripts/utils/ext.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/browser/src/scripts/utils/fetch.js b/browser/src/scripts/utils/fetch.js index 475ea5a7..f6c82acc 100644 --- a/browser/src/scripts/utils/fetch.js +++ b/browser/src/scripts/utils/fetch.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/browser/src/scripts/utils/services.ts b/browser/src/scripts/utils/services.ts index 52e22a14..4dd93a2c 100644 --- a/browser/src/scripts/utils/services.ts +++ b/browser/src/scripts/utils/services.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/browser/src/scripts/utils/storage.ts b/browser/src/scripts/utils/storage.ts index 0df5b02e..c83d26b6 100644 --- a/browser/src/scripts/utils/storage.ts +++ b/browser/src/scripts/utils/storage.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/browser/src/styles/popup.css b/browser/src/styles/popup.css index 4dec5270..daa4f6ca 100644 --- a/browser/src/styles/popup.css +++ b/browser/src/styles/popup.css @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/browser/src/styles/select.css b/browser/src/styles/select.css index 54024788..cbbc60fd 100644 --- a/browser/src/styles/select.css +++ b/browser/src/styles/select.css @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/browser/webpack.config.js b/browser/webpack.config.js index d2f2829d..4ee0d45b 100644 --- a/browser/webpack.config.js +++ b/browser/webpack.config.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/jslib/src/helpers/arr.spec.ts b/jslib/src/helpers/arr.spec.ts index 51ded98c..60d7e921 100644 --- a/jslib/src/helpers/arr.spec.ts +++ b/jslib/src/helpers/arr.spec.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/jslib/src/helpers/arr.ts b/jslib/src/helpers/arr.ts index cd2b72da..b80b1608 100644 --- a/jslib/src/helpers/arr.ts +++ b/jslib/src/helpers/arr.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/jslib/src/helpers/books.spec.ts b/jslib/src/helpers/books.spec.ts index 3356e95a..79037241 100644 --- a/jslib/src/helpers/books.spec.ts +++ b/jslib/src/helpers/books.spec.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/jslib/src/helpers/books.ts b/jslib/src/helpers/books.ts index 8b10b714..fbeaed60 100644 --- a/jslib/src/helpers/books.ts +++ b/jslib/src/helpers/books.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/jslib/src/helpers/filters.ts b/jslib/src/helpers/filters.ts index 9dbcacfe..390d2b31 100644 --- a/jslib/src/helpers/filters.ts +++ b/jslib/src/helpers/filters.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/jslib/src/helpers/http.ts b/jslib/src/helpers/http.ts index 1134d98c..6982bd35 100644 --- a/jslib/src/helpers/http.ts +++ b/jslib/src/helpers/http.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/jslib/src/helpers/index.ts b/jslib/src/helpers/index.ts index f7c60a43..8853e4f7 100644 --- a/jslib/src/helpers/index.ts +++ b/jslib/src/helpers/index.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/jslib/src/helpers/keyboard.ts b/jslib/src/helpers/keyboard.ts index 59af8198..5c5bc4f5 100644 --- a/jslib/src/helpers/keyboard.ts +++ b/jslib/src/helpers/keyboard.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/jslib/src/helpers/obj.ts b/jslib/src/helpers/obj.ts index 886de4e7..256353f7 100644 --- a/jslib/src/helpers/obj.ts +++ b/jslib/src/helpers/obj.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/jslib/src/helpers/perf.ts b/jslib/src/helpers/perf.ts index 8889d0de..77c71af3 100644 --- a/jslib/src/helpers/perf.ts +++ b/jslib/src/helpers/perf.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/jslib/src/helpers/queries.ts b/jslib/src/helpers/queries.ts index ec7d21ce..95bf11c2 100644 --- a/jslib/src/helpers/queries.ts +++ b/jslib/src/helpers/queries.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/jslib/src/helpers/search.spec.ts b/jslib/src/helpers/search.spec.ts index b6c28f16..ddc968f5 100644 --- a/jslib/src/helpers/search.spec.ts +++ b/jslib/src/helpers/search.spec.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/jslib/src/helpers/search.ts b/jslib/src/helpers/search.ts index a34b8eb4..1ab7744b 100644 --- a/jslib/src/helpers/search.ts +++ b/jslib/src/helpers/search.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/jslib/src/helpers/select.ts b/jslib/src/helpers/select.ts index acc9e8ee..999ee31d 100644 --- a/jslib/src/helpers/select.ts +++ b/jslib/src/helpers/select.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/jslib/src/helpers/url.ts b/jslib/src/helpers/url.ts index ef93cee0..fd3935c9 100644 --- a/jslib/src/helpers/url.ts +++ b/jslib/src/helpers/url.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/jslib/src/index.ts b/jslib/src/index.ts index 07df74b8..b5f9047e 100644 --- a/jslib/src/index.ts +++ b/jslib/src/index.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/jslib/src/operations/books.ts b/jslib/src/operations/books.ts index 1f955de5..0039d1df 100644 --- a/jslib/src/operations/books.ts +++ b/jslib/src/operations/books.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/jslib/src/operations/docs.ts b/jslib/src/operations/docs.ts index 49d8965c..f2b286ae 100644 --- a/jslib/src/operations/docs.ts +++ b/jslib/src/operations/docs.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/jslib/src/operations/index.ts b/jslib/src/operations/index.ts index 7a13f93f..eaf23296 100644 --- a/jslib/src/operations/index.ts +++ b/jslib/src/operations/index.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/jslib/src/operations/notes.ts b/jslib/src/operations/notes.ts index 8e7b03e8..bf9ce8fc 100644 --- a/jslib/src/operations/notes.ts +++ b/jslib/src/operations/notes.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/jslib/src/operations/types.ts b/jslib/src/operations/types.ts index 9a26d0a8..630c8f07 100644 --- a/jslib/src/operations/types.ts +++ b/jslib/src/operations/types.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/jslib/src/services/books.ts b/jslib/src/services/books.ts index 1940f04a..10a37a5d 100644 --- a/jslib/src/services/books.ts +++ b/jslib/src/services/books.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/jslib/src/services/index.ts b/jslib/src/services/index.ts index ec403fcc..746efc01 100644 --- a/jslib/src/services/index.ts +++ b/jslib/src/services/index.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/jslib/src/services/notes.ts b/jslib/src/services/notes.ts index 08da0f9a..d0419e25 100644 --- a/jslib/src/services/notes.ts +++ b/jslib/src/services/notes.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/jslib/src/services/types.ts b/jslib/src/services/types.ts index c9532a27..332ef21c 100644 --- a/jslib/src/services/types.ts +++ b/jslib/src/services/types.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/jslib/src/services/users.ts b/jslib/src/services/users.ts index ed2cd0c6..a3429ab7 100644 --- a/jslib/src/services/users.ts +++ b/jslib/src/services/users.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/assert/assert.go b/pkg/assert/assert.go index 24c65bd7..9551c683 100644 --- a/pkg/assert/assert.go +++ b/pkg/assert/assert.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/client/client.go b/pkg/cli/client/client.go index dfca8067..6c783d2e 100644 --- a/pkg/cli/client/client.go +++ b/pkg/cli/client/client.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/client/client_test.go b/pkg/cli/client/client_test.go index e54644a8..b11ccebe 100644 --- a/pkg/cli/client/client_test.go +++ b/pkg/cli/client/client_test.go @@ -1,3 +1,21 @@ +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd + * + * This file is part of Dnote. + * + * Dnote is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Dnote is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Dnote. If not, see . + */ + package client import ( diff --git a/pkg/cli/cmd/add/add.go b/pkg/cli/cmd/add/add.go index aee6e8de..b952481d 100644 --- a/pkg/cli/cmd/add/add.go +++ b/pkg/cli/cmd/add/add.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/cmd/cat/cat.go b/pkg/cli/cmd/cat/cat.go index 9adb5fe8..79463d9c 100644 --- a/pkg/cli/cmd/cat/cat.go +++ b/pkg/cli/cmd/cat/cat.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/cmd/edit/book.go b/pkg/cli/cmd/edit/book.go index b6371ae8..2f34ec01 100644 --- a/pkg/cli/cmd/edit/book.go +++ b/pkg/cli/cmd/edit/book.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/cmd/edit/edit.go b/pkg/cli/cmd/edit/edit.go index 55087ee4..0836658a 100644 --- a/pkg/cli/cmd/edit/edit.go +++ b/pkg/cli/cmd/edit/edit.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/cmd/edit/note.go b/pkg/cli/cmd/edit/note.go index 8c06ea4d..54075131 100644 --- a/pkg/cli/cmd/edit/note.go +++ b/pkg/cli/cmd/edit/note.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/cmd/find/find.go b/pkg/cli/cmd/find/find.go index 91d143d4..033f938f 100644 --- a/pkg/cli/cmd/find/find.go +++ b/pkg/cli/cmd/find/find.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/cmd/find/lexer.go b/pkg/cli/cmd/find/lexer.go index 00438c0a..8cf560ce 100644 --- a/pkg/cli/cmd/find/lexer.go +++ b/pkg/cli/cmd/find/lexer.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/cmd/find/lexer_test.go b/pkg/cli/cmd/find/lexer_test.go index 98ff8257..c687a62d 100644 --- a/pkg/cli/cmd/find/lexer_test.go +++ b/pkg/cli/cmd/find/lexer_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/cmd/login/login.go b/pkg/cli/cmd/login/login.go index bc68657a..9723a5be 100644 --- a/pkg/cli/cmd/login/login.go +++ b/pkg/cli/cmd/login/login.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/cmd/login/login_test.go b/pkg/cli/cmd/login/login_test.go index d48202ce..f57b5979 100644 --- a/pkg/cli/cmd/login/login_test.go +++ b/pkg/cli/cmd/login/login_test.go @@ -1,3 +1,21 @@ +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd + * + * This file is part of Dnote. + * + * Dnote is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Dnote is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Dnote. If not, see . + */ + package login import ( diff --git a/pkg/cli/cmd/logout/logout.go b/pkg/cli/cmd/logout/logout.go index 635034b6..ea647e72 100644 --- a/pkg/cli/cmd/logout/logout.go +++ b/pkg/cli/cmd/logout/logout.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/cmd/ls/ls.go b/pkg/cli/cmd/ls/ls.go index 86294856..f09f665a 100644 --- a/pkg/cli/cmd/ls/ls.go +++ b/pkg/cli/cmd/ls/ls.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/cmd/remove/remove.go b/pkg/cli/cmd/remove/remove.go index c6fbc62a..9da9bdd2 100644 --- a/pkg/cli/cmd/remove/remove.go +++ b/pkg/cli/cmd/remove/remove.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/cmd/root/root.go b/pkg/cli/cmd/root/root.go index 5e3db92f..ef1d0681 100644 --- a/pkg/cli/cmd/root/root.go +++ b/pkg/cli/cmd/root/root.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/cmd/sync/main_test.go b/pkg/cli/cmd/sync/main_test.go index c869afb7..90c8a323 100644 --- a/pkg/cli/cmd/sync/main_test.go +++ b/pkg/cli/cmd/sync/main_test.go @@ -1,3 +1,21 @@ +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd + * + * This file is part of Dnote. + * + * Dnote is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Dnote is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Dnote. If not, see . + */ + package sync import ( diff --git a/pkg/cli/cmd/sync/merge.go b/pkg/cli/cmd/sync/merge.go index 0e9c93eb..2ac41b37 100644 --- a/pkg/cli/cmd/sync/merge.go +++ b/pkg/cli/cmd/sync/merge.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/cmd/sync/merge_test.go b/pkg/cli/cmd/sync/merge_test.go index f2954581..3203adfc 100644 --- a/pkg/cli/cmd/sync/merge_test.go +++ b/pkg/cli/cmd/sync/merge_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/cmd/sync/sync.go b/pkg/cli/cmd/sync/sync.go index f5df00f3..6dbd641d 100644 --- a/pkg/cli/cmd/sync/sync.go +++ b/pkg/cli/cmd/sync/sync.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/cmd/sync/sync_test.go b/pkg/cli/cmd/sync/sync_test.go index f107ed15..9ae51bde 100644 --- a/pkg/cli/cmd/sync/sync_test.go +++ b/pkg/cli/cmd/sync/sync_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/cmd/version/version.go b/pkg/cli/cmd/version/version.go index b7b29ba0..cbafeaca 100644 --- a/pkg/cli/cmd/version/version.go +++ b/pkg/cli/cmd/version/version.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/cmd/view/view.go b/pkg/cli/cmd/view/view.go index 1794560c..434d5e08 100644 --- a/pkg/cli/cmd/view/view.go +++ b/pkg/cli/cmd/view/view.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/config/config.go b/pkg/cli/config/config.go index 8d0bd7dd..09c539df 100644 --- a/pkg/cli/config/config.go +++ b/pkg/cli/config/config.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/consts/consts.go b/pkg/cli/consts/consts.go index e7235934..05ee4408 100644 --- a/pkg/cli/consts/consts.go +++ b/pkg/cli/consts/consts.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/context/ctx.go b/pkg/cli/context/ctx.go index 6ba8f9dd..b42bed32 100644 --- a/pkg/cli/context/ctx.go +++ b/pkg/cli/context/ctx.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/context/testutils.go b/pkg/cli/context/testutils.go index 4572a62c..0b6962db 100644 --- a/pkg/cli/context/testutils.go +++ b/pkg/cli/context/testutils.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/crypt/crypto.go b/pkg/cli/crypt/crypto.go index d0fba497..fbed118a 100644 --- a/pkg/cli/crypt/crypto.go +++ b/pkg/cli/crypt/crypto.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/crypt/crypto_test.go b/pkg/cli/crypt/crypto_test.go index 013c6110..c1e91f4c 100644 --- a/pkg/cli/crypt/crypto_test.go +++ b/pkg/cli/crypt/crypto_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/database/models.go b/pkg/cli/database/models.go index 2476651c..df27fa1c 100644 --- a/pkg/cli/database/models.go +++ b/pkg/cli/database/models.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/database/models_test.go b/pkg/cli/database/models_test.go index 6ff6d775..5639048a 100644 --- a/pkg/cli/database/models_test.go +++ b/pkg/cli/database/models_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/database/queries.go b/pkg/cli/database/queries.go index 5691175f..1e04e521 100644 --- a/pkg/cli/database/queries.go +++ b/pkg/cli/database/queries.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/database/queries_test.go b/pkg/cli/database/queries_test.go index 1ac168e4..7cc6a938 100644 --- a/pkg/cli/database/queries_test.go +++ b/pkg/cli/database/queries_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/database/sql.go b/pkg/cli/database/sql.go index 3af29502..d237752b 100644 --- a/pkg/cli/database/sql.go +++ b/pkg/cli/database/sql.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/database/testutils.go b/pkg/cli/database/testutils.go index dbcb0bc5..26f159f3 100644 --- a/pkg/cli/database/testutils.go +++ b/pkg/cli/database/testutils.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/dirs/dirs.go b/pkg/cli/dirs/dirs.go index 81f2e5f7..07585224 100644 --- a/pkg/cli/dirs/dirs.go +++ b/pkg/cli/dirs/dirs.go @@ -1,3 +1,21 @@ +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd + * + * This file is part of Dnote. + * + * Dnote is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Dnote is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Dnote. If not, see . + */ + // Package dirs provides base directory definitions for the system package dirs diff --git a/pkg/cli/dirs/dirs_test.go b/pkg/cli/dirs/dirs_test.go index 058b7949..486b09bd 100644 --- a/pkg/cli/dirs/dirs_test.go +++ b/pkg/cli/dirs/dirs_test.go @@ -1,3 +1,21 @@ +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd + * + * This file is part of Dnote. + * + * Dnote is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Dnote is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Dnote. If not, see . + */ + package dirs import ( diff --git a/pkg/cli/dirs/dirs_unix.go b/pkg/cli/dirs/dirs_unix.go index 28dfe5fb..faf4682b 100644 --- a/pkg/cli/dirs/dirs_unix.go +++ b/pkg/cli/dirs/dirs_unix.go @@ -1,3 +1,21 @@ +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd + * + * This file is part of Dnote. + * + * Dnote is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Dnote is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Dnote. If not, see . + */ + // +build linux darwin package dirs diff --git a/pkg/cli/dirs/dirs_unix_test.go b/pkg/cli/dirs/dirs_unix_test.go index a0b08c0c..90c9e6d5 100644 --- a/pkg/cli/dirs/dirs_unix_test.go +++ b/pkg/cli/dirs/dirs_unix_test.go @@ -1,3 +1,21 @@ +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd + * + * This file is part of Dnote. + * + * Dnote is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Dnote is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Dnote. If not, see . + */ + // +build linux darwin package dirs diff --git a/pkg/cli/dirs/dirs_windows.go b/pkg/cli/dirs/dirs_windows.go index a3660e18..954410b8 100644 --- a/pkg/cli/dirs/dirs_windows.go +++ b/pkg/cli/dirs/dirs_windows.go @@ -1,3 +1,21 @@ +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd + * + * This file is part of Dnote. + * + * Dnote is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Dnote is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Dnote. If not, see . + */ + // +build windows package dirs diff --git a/pkg/cli/dirs/dirs_windows_test.go b/pkg/cli/dirs/dirs_windows_test.go index 093c1608..c304c07f 100644 --- a/pkg/cli/dirs/dirs_windows_test.go +++ b/pkg/cli/dirs/dirs_windows_test.go @@ -1,3 +1,21 @@ +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd + * + * This file is part of Dnote. + * + * Dnote is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Dnote is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Dnote. If not, see . + */ + // +build windows package dirs diff --git a/pkg/cli/infra/init.go b/pkg/cli/infra/init.go index 381483a3..6ac99472 100644 --- a/pkg/cli/infra/init.go +++ b/pkg/cli/infra/init.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/infra/init_test.go b/pkg/cli/infra/init_test.go index 723ceaf2..a072ba25 100644 --- a/pkg/cli/infra/init_test.go +++ b/pkg/cli/infra/init_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/log/log.go b/pkg/cli/log/log.go index 93a76214..884475fc 100644 --- a/pkg/cli/log/log.go +++ b/pkg/cli/log/log.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/main.go b/pkg/cli/main.go index 452dc76e..253f0cfb 100644 --- a/pkg/cli/main.go +++ b/pkg/cli/main.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/main_test.go b/pkg/cli/main_test.go index 38b8bdcf..e01cf34a 100644 --- a/pkg/cli/main_test.go +++ b/pkg/cli/main_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/migrate/legacy.go b/pkg/cli/migrate/legacy.go index ef4f8045..512ba2a9 100644 --- a/pkg/cli/migrate/legacy.go +++ b/pkg/cli/migrate/legacy.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/migrate/legacy_test.go b/pkg/cli/migrate/legacy_test.go index 948dd7b4..12f27eb9 100644 --- a/pkg/cli/migrate/legacy_test.go +++ b/pkg/cli/migrate/legacy_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/migrate/migrate.go b/pkg/cli/migrate/migrate.go index 431360a4..fa7bd2e2 100644 --- a/pkg/cli/migrate/migrate.go +++ b/pkg/cli/migrate/migrate.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/migrate/migrate_test.go b/pkg/cli/migrate/migrate_test.go index 6a2719b0..6fd5264b 100644 --- a/pkg/cli/migrate/migrate_test.go +++ b/pkg/cli/migrate/migrate_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/migrate/migrations.go b/pkg/cli/migrate/migrations.go index c6b8e5ff..9a35d824 100644 --- a/pkg/cli/migrate/migrations.go +++ b/pkg/cli/migrate/migrations.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/output/output.go b/pkg/cli/output/output.go index 6438b740..ddd022ef 100644 --- a/pkg/cli/output/output.go +++ b/pkg/cli/output/output.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/testutils/main.go b/pkg/cli/testutils/main.go index dfc62421..d6490b49 100644 --- a/pkg/cli/testutils/main.go +++ b/pkg/cli/testutils/main.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/testutils/setup.go b/pkg/cli/testutils/setup.go index 33d67fa1..20c8a0a1 100644 --- a/pkg/cli/testutils/setup.go +++ b/pkg/cli/testutils/setup.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/ui/editor.go b/pkg/cli/ui/editor.go index 9fa73072..82ab3f3c 100644 --- a/pkg/cli/ui/editor.go +++ b/pkg/cli/ui/editor.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/ui/editor_test.go b/pkg/cli/ui/editor_test.go index 03338560..99a4760f 100644 --- a/pkg/cli/ui/editor_test.go +++ b/pkg/cli/ui/editor_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/ui/terminal.go b/pkg/cli/ui/terminal.go index e172343e..d29ba8bf 100644 --- a/pkg/cli/ui/terminal.go +++ b/pkg/cli/ui/terminal.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/upgrade/upgrade.go b/pkg/cli/upgrade/upgrade.go index d56e8852..4a967640 100644 --- a/pkg/cli/upgrade/upgrade.go +++ b/pkg/cli/upgrade/upgrade.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/upgrade/upgrade_test.go b/pkg/cli/upgrade/upgrade_test.go index f122ff28..98a67891 100644 --- a/pkg/cli/upgrade/upgrade_test.go +++ b/pkg/cli/upgrade/upgrade_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/utils/diff/diff.go b/pkg/cli/utils/diff/diff.go index 774732fe..f427cfa8 100644 --- a/pkg/cli/utils/diff/diff.go +++ b/pkg/cli/utils/diff/diff.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/utils/diff/diff_test.go b/pkg/cli/utils/diff/diff_test.go index 8ad274b9..2893c80b 100644 --- a/pkg/cli/utils/diff/diff_test.go +++ b/pkg/cli/utils/diff/diff_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/utils/files.go b/pkg/cli/utils/files.go index 28eff024..24d27448 100644 --- a/pkg/cli/utils/files.go +++ b/pkg/cli/utils/files.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/utils/utils.go b/pkg/cli/utils/utils.go index dfff484b..4f75d4be 100644 --- a/pkg/cli/utils/utils.go +++ b/pkg/cli/utils/utils.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/validate/book_test.go b/pkg/cli/validate/book_test.go index f26d51ac..9a716a66 100644 --- a/pkg/cli/validate/book_test.go +++ b/pkg/cli/validate/book_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/cli/validate/books.go b/pkg/cli/validate/books.go index c220e0c9..fbc38fce 100644 --- a/pkg/cli/validate/books.go +++ b/pkg/cli/validate/books.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/clock/clock.go b/pkg/clock/clock.go index a1a4ad26..de839659 100644 --- a/pkg/clock/clock.go +++ b/pkg/clock/clock.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/api/auth.go b/pkg/server/api/auth.go index 914e1233..56425c87 100644 --- a/pkg/server/api/auth.go +++ b/pkg/server/api/auth.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/api/auth_test.go b/pkg/server/api/auth_test.go index ec5666db..1e75dd92 100644 --- a/pkg/server/api/auth_test.go +++ b/pkg/server/api/auth_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/api/health.go b/pkg/server/api/health.go index 739e8859..3480b796 100644 --- a/pkg/server/api/health.go +++ b/pkg/server/api/health.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/api/health_test.go b/pkg/server/api/health_test.go index 193a2cd9..0deadc5e 100644 --- a/pkg/server/api/health_test.go +++ b/pkg/server/api/health_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/api/helpers.go b/pkg/server/api/helpers.go index 8137939f..60c990fd 100644 --- a/pkg/server/api/helpers.go +++ b/pkg/server/api/helpers.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/api/main_test.go b/pkg/server/api/main_test.go index ee70a07d..cca6ae29 100644 --- a/pkg/server/api/main_test.go +++ b/pkg/server/api/main_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/api/notes.go b/pkg/server/api/notes.go index 8a745e41..d87c2ff7 100644 --- a/pkg/server/api/notes.go +++ b/pkg/server/api/notes.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/api/notes_test.go b/pkg/server/api/notes_test.go index d0b0617b..c47caf4e 100644 --- a/pkg/server/api/notes_test.go +++ b/pkg/server/api/notes_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/api/routes.go b/pkg/server/api/routes.go index a1d8385d..d1b771bf 100644 --- a/pkg/server/api/routes.go +++ b/pkg/server/api/routes.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/api/routes_test.go b/pkg/server/api/routes_test.go index 8a395f01..7b8f5ec2 100644 --- a/pkg/server/api/routes_test.go +++ b/pkg/server/api/routes_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/api/testutils.go b/pkg/server/api/testutils.go index b8896475..fe17e25c 100644 --- a/pkg/server/api/testutils.go +++ b/pkg/server/api/testutils.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/api/user.go b/pkg/server/api/user.go index 04e41149..d4a8ec51 100644 --- a/pkg/server/api/user.go +++ b/pkg/server/api/user.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/api/user_test.go b/pkg/server/api/user_test.go index 19376300..95df0179 100644 --- a/pkg/server/api/user_test.go +++ b/pkg/server/api/user_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/api/v3_auth.go b/pkg/server/api/v3_auth.go index 1c9ab145..580f82e1 100644 --- a/pkg/server/api/v3_auth.go +++ b/pkg/server/api/v3_auth.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/api/v3_auth_test.go b/pkg/server/api/v3_auth_test.go index f08eea2b..2aa4761e 100644 --- a/pkg/server/api/v3_auth_test.go +++ b/pkg/server/api/v3_auth_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/api/v3_books.go b/pkg/server/api/v3_books.go index 962d38af..34985bb5 100644 --- a/pkg/server/api/v3_books.go +++ b/pkg/server/api/v3_books.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/api/v3_books_test.go b/pkg/server/api/v3_books_test.go index 0dbeaae3..4cd6d579 100644 --- a/pkg/server/api/v3_books_test.go +++ b/pkg/server/api/v3_books_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/api/v3_notes.go b/pkg/server/api/v3_notes.go index b6e7c750..c38ced5a 100644 --- a/pkg/server/api/v3_notes.go +++ b/pkg/server/api/v3_notes.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/api/v3_notes_test.go b/pkg/server/api/v3_notes_test.go index 3c57fe27..df30beed 100644 --- a/pkg/server/api/v3_notes_test.go +++ b/pkg/server/api/v3_notes_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/api/v3_sync.go b/pkg/server/api/v3_sync.go index a04f41fa..f30f4273 100644 --- a/pkg/server/api/v3_sync.go +++ b/pkg/server/api/v3_sync.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/api/v3_sync_test.go b/pkg/server/api/v3_sync_test.go index 2da52aac..8f2fb6e6 100644 --- a/pkg/server/api/v3_sync_test.go +++ b/pkg/server/api/v3_sync_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/app/app.go b/pkg/server/app/app.go index 8f1b50f8..c530d4fa 100644 --- a/pkg/server/app/app.go +++ b/pkg/server/app/app.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/app/books.go b/pkg/server/app/books.go index 2fa0a9b1..f84f4d84 100644 --- a/pkg/server/app/books.go +++ b/pkg/server/app/books.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/app/books_test.go b/pkg/server/app/books_test.go index 17c2ddcb..3219320a 100644 --- a/pkg/server/app/books_test.go +++ b/pkg/server/app/books_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/app/doc.go b/pkg/server/app/doc.go index b1e3a101..c8cebd60 100644 --- a/pkg/server/app/doc.go +++ b/pkg/server/app/doc.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/app/email.go b/pkg/server/app/email.go index 41bd8287..64485340 100644 --- a/pkg/server/app/email.go +++ b/pkg/server/app/email.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/app/email_test.go b/pkg/server/app/email_test.go index 856beeda..f41d5a3d 100644 --- a/pkg/server/app/email_test.go +++ b/pkg/server/app/email_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/app/helpers.go b/pkg/server/app/helpers.go index ea72d107..024dd7d3 100644 --- a/pkg/server/app/helpers.go +++ b/pkg/server/app/helpers.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/app/helpers_test.go b/pkg/server/app/helpers_test.go index b0b32d23..370944e8 100644 --- a/pkg/server/app/helpers_test.go +++ b/pkg/server/app/helpers_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/app/main_test.go b/pkg/server/app/main_test.go index b3e34574..e9a8ebc3 100644 --- a/pkg/server/app/main_test.go +++ b/pkg/server/app/main_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/app/notes.go b/pkg/server/app/notes.go index f3dbc86e..054a4989 100644 --- a/pkg/server/app/notes.go +++ b/pkg/server/app/notes.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/app/notes_test.go b/pkg/server/app/notes_test.go index 4ce6c701..bfb150c1 100644 --- a/pkg/server/app/notes_test.go +++ b/pkg/server/app/notes_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/app/sessions.go b/pkg/server/app/sessions.go index d4831053..81a1b255 100644 --- a/pkg/server/app/sessions.go +++ b/pkg/server/app/sessions.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/app/testutils.go b/pkg/server/app/testutils.go index 8accf480..6645e430 100644 --- a/pkg/server/app/testutils.go +++ b/pkg/server/app/testutils.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/app/users.go b/pkg/server/app/users.go index ca902775..9261dcb9 100644 --- a/pkg/server/app/users.go +++ b/pkg/server/app/users.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/app/users_test.go b/pkg/server/app/users_test.go index 580bed25..b78458f6 100644 --- a/pkg/server/app/users_test.go +++ b/pkg/server/app/users_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/config/config.go b/pkg/server/config/config.go index 0ed705d1..c3e5eaaa 100644 --- a/pkg/server/config/config.go +++ b/pkg/server/config/config.go @@ -1,3 +1,21 @@ +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd + * + * This file is part of Dnote. + * + * Dnote is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Dnote is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Dnote. If not, see . + */ + package config import ( diff --git a/pkg/server/config/config_test.go b/pkg/server/config/config_test.go index b86841f4..10f5fd12 100644 --- a/pkg/server/config/config_test.go +++ b/pkg/server/config/config_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/crypt/crypt.go b/pkg/server/crypt/crypt.go index e720daea..20150050 100644 --- a/pkg/server/crypt/crypt.go +++ b/pkg/server/crypt/crypt.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/database/consts.go b/pkg/server/database/consts.go index 69f04711..3b2f8a06 100644 --- a/pkg/server/database/consts.go +++ b/pkg/server/database/consts.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/database/database.go b/pkg/server/database/database.go index 30eea6f5..dabb8ff1 100644 --- a/pkg/server/database/database.go +++ b/pkg/server/database/database.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/database/migrate.go b/pkg/server/database/migrate.go index f26a1400..05e88b18 100644 --- a/pkg/server/database/migrate.go +++ b/pkg/server/database/migrate.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/database/migrate/main.go b/pkg/server/database/migrate/main.go index 70614185..4827438c 100644 --- a/pkg/server/database/migrate/main.go +++ b/pkg/server/database/migrate/main.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/database/models.go b/pkg/server/database/models.go index c7905158..2b228bdc 100644 --- a/pkg/server/database/models.go +++ b/pkg/server/database/models.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/database/notes.go b/pkg/server/database/notes.go index 91a850ad..43620474 100644 --- a/pkg/server/database/notes.go +++ b/pkg/server/database/notes.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/database/types.go b/pkg/server/database/types.go index e230c5ac..baaffe14 100644 --- a/pkg/server/database/types.go +++ b/pkg/server/database/types.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/handlers/auth.go b/pkg/server/handlers/auth.go index 2917e1b7..83946dce 100644 --- a/pkg/server/handlers/auth.go +++ b/pkg/server/handlers/auth.go @@ -1,3 +1,21 @@ +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd + * + * This file is part of Dnote. + * + * Dnote is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Dnote is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Dnote. If not, see . + */ + package handlers import ( diff --git a/pkg/server/handlers/helpers.go b/pkg/server/handlers/helpers.go index 4a274941..8c0d51a7 100644 --- a/pkg/server/handlers/helpers.go +++ b/pkg/server/handlers/helpers.go @@ -1,3 +1,21 @@ +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd + * + * This file is part of Dnote. + * + * Dnote is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Dnote is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Dnote. If not, see . + */ + package handlers import ( diff --git a/pkg/server/handlers/helpers_test.go b/pkg/server/handlers/helpers_test.go index 9c1121bc..f82e6ea0 100644 --- a/pkg/server/handlers/helpers_test.go +++ b/pkg/server/handlers/helpers_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/handlers/limit.go b/pkg/server/handlers/limit.go index 91d8685a..9859e7e2 100644 --- a/pkg/server/handlers/limit.go +++ b/pkg/server/handlers/limit.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/handlers/logging.go b/pkg/server/handlers/logging.go index d809fd1f..98e36597 100644 --- a/pkg/server/handlers/logging.go +++ b/pkg/server/handlers/logging.go @@ -1,3 +1,21 @@ +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd + * + * This file is part of Dnote. + * + * Dnote is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Dnote is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Dnote. If not, see . + */ + package handlers import ( diff --git a/pkg/server/handlers/main_test.go b/pkg/server/handlers/main_test.go index 1263b952..550c919e 100644 --- a/pkg/server/handlers/main_test.go +++ b/pkg/server/handlers/main_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/helpers/const.go b/pkg/server/helpers/const.go index e589ccb5..7d639a14 100644 --- a/pkg/server/helpers/const.go +++ b/pkg/server/helpers/const.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/helpers/helpers.go b/pkg/server/helpers/helpers.go index bf1a88dc..b96b7b11 100644 --- a/pkg/server/helpers/helpers.go +++ b/pkg/server/helpers/helpers.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/job/job.go b/pkg/server/job/job.go index 15450c00..45990a49 100644 --- a/pkg/server/job/job.go +++ b/pkg/server/job/job.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/job/job_test.go b/pkg/server/job/job_test.go index 3ebfa90b..fbfb0a62 100644 --- a/pkg/server/job/job_test.go +++ b/pkg/server/job/job_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/job/remind/inactive.go b/pkg/server/job/remind/inactive.go index e7fc0f03..d0e9de01 100644 --- a/pkg/server/job/remind/inactive.go +++ b/pkg/server/job/remind/inactive.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/job/remind/inactive_test.go b/pkg/server/job/remind/inactive_test.go index 2f63cdcf..d79095c5 100644 --- a/pkg/server/job/remind/inactive_test.go +++ b/pkg/server/job/remind/inactive_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/job/remind/main_test.go b/pkg/server/job/remind/main_test.go index c92e1bef..d8b6b62a 100644 --- a/pkg/server/job/remind/main_test.go +++ b/pkg/server/job/remind/main_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/log/log.go b/pkg/server/log/log.go index 607ece7c..057419b8 100644 --- a/pkg/server/log/log.go +++ b/pkg/server/log/log.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/mailer/backend.go b/pkg/server/mailer/backend.go index fddf1e98..2afef065 100644 --- a/pkg/server/mailer/backend.go +++ b/pkg/server/mailer/backend.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/mailer/mailer.go b/pkg/server/mailer/mailer.go index 60a95bac..b867a00b 100644 --- a/pkg/server/mailer/mailer.go +++ b/pkg/server/mailer/mailer.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/mailer/mailer_test.go b/pkg/server/mailer/mailer_test.go index 79d5d29c..47814fb2 100644 --- a/pkg/server/mailer/mailer_test.go +++ b/pkg/server/mailer/mailer_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/mailer/templates/main.go b/pkg/server/mailer/templates/main.go index 5d3d055e..3ec5ce99 100644 --- a/pkg/server/mailer/templates/main.go +++ b/pkg/server/mailer/templates/main.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/mailer/tokens.go b/pkg/server/mailer/tokens.go index a2ae431c..8d0d0cd5 100644 --- a/pkg/server/mailer/tokens.go +++ b/pkg/server/mailer/tokens.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/mailer/types.go b/pkg/server/mailer/types.go index 5306fed6..8992902e 100644 --- a/pkg/server/mailer/types.go +++ b/pkg/server/mailer/types.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/main.go b/pkg/server/main.go index a3108760..f1721009 100644 --- a/pkg/server/main.go +++ b/pkg/server/main.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/operations/doc.go b/pkg/server/operations/doc.go index 9aa0b213..d2947e5c 100644 --- a/pkg/server/operations/doc.go +++ b/pkg/server/operations/doc.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/operations/main_test.go b/pkg/server/operations/main_test.go index 5421d78f..dc79ba9c 100644 --- a/pkg/server/operations/main_test.go +++ b/pkg/server/operations/main_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/operations/notes.go b/pkg/server/operations/notes.go index 977236ba..22bb8468 100644 --- a/pkg/server/operations/notes.go +++ b/pkg/server/operations/notes.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/operations/notes_test.go b/pkg/server/operations/notes_test.go index a9f1e816..0ea93878 100644 --- a/pkg/server/operations/notes_test.go +++ b/pkg/server/operations/notes_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/permissions/permissions.go b/pkg/server/permissions/permissions.go index e3da80f0..bc89ea26 100644 --- a/pkg/server/permissions/permissions.go +++ b/pkg/server/permissions/permissions.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/permissions/permissions_test.go b/pkg/server/permissions/permissions_test.go index 3508400c..d67089fe 100644 --- a/pkg/server/permissions/permissions_test.go +++ b/pkg/server/permissions/permissions_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/presenters/book.go b/pkg/server/presenters/book.go index edb1d57c..5d5e6b47 100644 --- a/pkg/server/presenters/book.go +++ b/pkg/server/presenters/book.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/presenters/email_preference.go b/pkg/server/presenters/email_preference.go index 66569132..f9ebee26 100644 --- a/pkg/server/presenters/email_preference.go +++ b/pkg/server/presenters/email_preference.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/presenters/helpers.go b/pkg/server/presenters/helpers.go index 1055ea39..c4a24a9c 100644 --- a/pkg/server/presenters/helpers.go +++ b/pkg/server/presenters/helpers.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/presenters/note.go b/pkg/server/presenters/note.go index 0da9d7a4..75a49081 100644 --- a/pkg/server/presenters/note.go +++ b/pkg/server/presenters/note.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/session/session.go b/pkg/server/session/session.go index cf0eacd2..08d8535d 100644 --- a/pkg/server/session/session.go +++ b/pkg/server/session/session.go @@ -1,3 +1,21 @@ +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd + * + * This file is part of Dnote. + * + * Dnote is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Dnote is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Dnote. If not, see . + */ + package session import ( diff --git a/pkg/server/session/session_test.go b/pkg/server/session/session_test.go index bd05f51e..21711c72 100644 --- a/pkg/server/session/session_test.go +++ b/pkg/server/session/session_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/testutils/main.go b/pkg/server/testutils/main.go index ba3cd5ae..018b95c4 100644 --- a/pkg/server/testutils/main.go +++ b/pkg/server/testutils/main.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/tmpl/app.go b/pkg/server/tmpl/app.go index a5483165..b45c9436 100644 --- a/pkg/server/tmpl/app.go +++ b/pkg/server/tmpl/app.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/tmpl/app_test.go b/pkg/server/tmpl/app_test.go index 6a1f08f5..0c59e900 100644 --- a/pkg/server/tmpl/app_test.go +++ b/pkg/server/tmpl/app_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/tmpl/data.go b/pkg/server/tmpl/data.go index 16c42e81..bee3d785 100644 --- a/pkg/server/tmpl/data.go +++ b/pkg/server/tmpl/data.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/tmpl/data_test.go b/pkg/server/tmpl/data_test.go index 768baf80..5b08d7fb 100644 --- a/pkg/server/tmpl/data_test.go +++ b/pkg/server/tmpl/data_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/tmpl/main_test.go b/pkg/server/tmpl/main_test.go index 25a9741c..6f75184f 100644 --- a/pkg/server/tmpl/main_test.go +++ b/pkg/server/tmpl/main_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/tmpl/tmpl.go b/pkg/server/tmpl/tmpl.go index 2b86009a..9719f0ae 100644 --- a/pkg/server/tmpl/tmpl.go +++ b/pkg/server/tmpl/tmpl.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/token/main_test.go b/pkg/server/token/main_test.go index b9cfb697..70d6fb96 100644 --- a/pkg/server/token/main_test.go +++ b/pkg/server/token/main_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/token/token.go b/pkg/server/token/token.go index 44cad090..c6b8438b 100644 --- a/pkg/server/token/token.go +++ b/pkg/server/token/token.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/token/token_test.go b/pkg/server/token/token_test.go index 79c643da..bc7e2364 100644 --- a/pkg/server/token/token_test.go +++ b/pkg/server/token/token_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/web/handlers.go b/pkg/server/web/handlers.go index 870408a8..854dbbb7 100644 --- a/pkg/server/web/handlers.go +++ b/pkg/server/web/handlers.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/web/handlers_test.go b/pkg/server/web/handlers_test.go index 897d97b2..dc83f507 100644 --- a/pkg/server/web/handlers_test.go +++ b/pkg/server/web/handlers_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/server/web/main_test.go b/pkg/server/web/main_test.go index b398db05..9c33b0f1 100644 --- a/pkg/server/web/main_test.go +++ b/pkg/server/web/main_test.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/pkg/watcher/main.go b/pkg/watcher/main.go index c03c1073..041fb774 100644 --- a/pkg/watcher/main.go +++ b/pkg/watcher/main.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/scripts/license.sh b/scripts/license.sh index 784270a6..8c4873ae 100755 --- a/scripts/license.sh +++ b/scripts/license.sh @@ -19,7 +19,7 @@ q END } -gpl="/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +gpl="/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * @@ -37,7 +37,7 @@ gpl="/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd * along with Dnote. If not, see . */" -agpl="/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +agpl="/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/assets/service-worker.js b/web/assets/service-worker.js index 365b2a32..9aeb1623 100644 --- a/web/assets/service-worker.js +++ b/web/assets/service-worker.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/declrations.d.ts b/web/declrations.d.ts index 08b5aef0..710f09ff 100644 --- a/web/declrations.d.ts +++ b/web/declrations.d.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/jest.config.js b/web/jest.config.js index a9365dee..5c0b4021 100644 --- a/web/jest.config.js +++ b/web/jest.config.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/client.tsx b/web/src/client.tsx index cb9dea37..2ea4fab1 100644 --- a/web/src/client.tsx +++ b/web/src/client.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/App/App.global.scss b/web/src/components/App/App.global.scss index 70abfe1c..58f45c4c 100644 --- a/web/src/components/App/App.global.scss +++ b/web/src/components/App/App.global.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/App/App.scss b/web/src/components/App/App.scss index 5e257c26..2417078d 100644 --- a/web/src/components/App/App.scss +++ b/web/src/components/App/App.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/App/HeaderData.tsx b/web/src/components/App/HeaderData.tsx index 6e7f4d7e..38715092 100644 --- a/web/src/components/App/HeaderData.tsx +++ b/web/src/components/App/HeaderData.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/App/_bootstrap.scss b/web/src/components/App/_bootstrap.scss index 25de43b3..cf403476 100644 --- a/web/src/components/App/_bootstrap.scss +++ b/web/src/components/App/_bootstrap.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/App/_buttons.scss b/web/src/components/App/_buttons.scss index 6178c0ce..6503ae1a 100644 --- a/web/src/components/App/_buttons.scss +++ b/web/src/components/App/_buttons.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/App/_font.scss b/web/src/components/App/_font.scss index 1f0b90d5..231d5024 100644 --- a/web/src/components/App/_font.scss +++ b/web/src/components/App/_font.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/App/_grid.scss b/web/src/components/App/_grid.scss index 5f25b11c..2f4beb03 100644 --- a/web/src/components/App/_grid.scss +++ b/web/src/components/App/_grid.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/App/_hljs.scss b/web/src/components/App/_hljs.scss index 03636f1a..877b5ae0 100644 --- a/web/src/components/App/_hljs.scss +++ b/web/src/components/App/_hljs.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/App/_markdown.scss b/web/src/components/App/_markdown.scss index 99b3e92f..9f928ad0 100644 --- a/web/src/components/App/_markdown.scss +++ b/web/src/components/App/_markdown.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/App/_marker.scss b/web/src/components/App/_marker.scss index 75afcc10..0129d3c4 100644 --- a/web/src/components/App/_marker.scss +++ b/web/src/components/App/_marker.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/App/_reboot.scss b/web/src/components/App/_reboot.scss index 174f2989..26f437c2 100644 --- a/web/src/components/App/_reboot.scss +++ b/web/src/components/App/_reboot.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/App/_rem.scss b/web/src/components/App/_rem.scss index c7e307b4..35579fa1 100644 --- a/web/src/components/App/_rem.scss +++ b/web/src/components/App/_rem.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/App/_responsive.scss b/web/src/components/App/_responsive.scss index caf5bc12..66dadb40 100644 --- a/web/src/components/App/_responsive.scss +++ b/web/src/components/App/_responsive.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/App/_select.scss b/web/src/components/App/_select.scss index f534a64a..b829056c 100644 --- a/web/src/components/App/_select.scss +++ b/web/src/components/App/_select.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/App/_shared.scss b/web/src/components/App/_shared.scss index 1ce9ee43..3198fdf7 100644 --- a/web/src/components/App/_shared.scss +++ b/web/src/components/App/_shared.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/App/_theme.scss b/web/src/components/App/_theme.scss index a3e09996..71d4a57a 100644 --- a/web/src/components/App/_theme.scss +++ b/web/src/components/App/_theme.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/App/_variables.scss b/web/src/components/App/_variables.scss index 0a2907db..808e20f7 100644 --- a/web/src/components/App/_variables.scss +++ b/web/src/components/App/_variables.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/App/index.tsx b/web/src/components/App/index.tsx index 092b77e3..53d00651 100644 --- a/web/src/components/App/index.tsx +++ b/web/src/components/App/index.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Books/BookHolder.scss b/web/src/components/Books/BookHolder.scss index a7293977..437b25d3 100644 --- a/web/src/components/Books/BookHolder.scss +++ b/web/src/components/Books/BookHolder.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Books/BookHolder.tsx b/web/src/components/Books/BookHolder.tsx index 97bae34e..6ed7cada 100644 --- a/web/src/components/Books/BookHolder.tsx +++ b/web/src/components/Books/BookHolder.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Books/BookItem/Actions.scss b/web/src/components/Books/BookItem/Actions.scss index 780cf465..21ccd688 100644 --- a/web/src/components/Books/BookItem/Actions.scss +++ b/web/src/components/Books/BookItem/Actions.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Books/BookItem/Actions.tsx b/web/src/components/Books/BookItem/Actions.tsx index 4fd92132..c6b9329d 100644 --- a/web/src/components/Books/BookItem/Actions.tsx +++ b/web/src/components/Books/BookItem/Actions.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Books/BookItem/BookItem.scss b/web/src/components/Books/BookItem/BookItem.scss index 56baf64a..e4436016 100644 --- a/web/src/components/Books/BookItem/BookItem.scss +++ b/web/src/components/Books/BookItem/BookItem.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Books/BookItem/index.tsx b/web/src/components/Books/BookItem/index.tsx index 43620855..01cc72b3 100644 --- a/web/src/components/Books/BookItem/index.tsx +++ b/web/src/components/Books/BookItem/index.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Books/BookList.scss b/web/src/components/Books/BookList.scss index 0e60b240..28e1abe7 100644 --- a/web/src/components/Books/BookList.scss +++ b/web/src/components/Books/BookList.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Books/BookList.tsx b/web/src/components/Books/BookList.tsx index 09741533..d024cba4 100644 --- a/web/src/components/Books/BookList.tsx +++ b/web/src/components/Books/BookList.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Books/Books.scss b/web/src/components/Books/Books.scss index 8d2e3de5..3711a920 100644 --- a/web/src/components/Books/Books.scss +++ b/web/src/components/Books/Books.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Books/Content.scss b/web/src/components/Books/Content.scss index af023f86..7b60c3e6 100644 --- a/web/src/components/Books/Content.scss +++ b/web/src/components/Books/Content.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Books/Content.tsx b/web/src/components/Books/Content.tsx index 74b7d4d3..f14557f0 100644 --- a/web/src/components/Books/Content.tsx +++ b/web/src/components/Books/Content.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Books/CreateBookButton.scss b/web/src/components/Books/CreateBookButton.scss index 90605230..1503d2b0 100644 --- a/web/src/components/Books/CreateBookButton.scss +++ b/web/src/components/Books/CreateBookButton.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Books/CreateBookButton.tsx b/web/src/components/Books/CreateBookButton.tsx index 1eb759a6..383fb918 100644 --- a/web/src/components/Books/CreateBookButton.tsx +++ b/web/src/components/Books/CreateBookButton.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Books/CreateBookModal.scss b/web/src/components/Books/CreateBookModal.scss index e97115e0..bf728964 100644 --- a/web/src/components/Books/CreateBookModal.scss +++ b/web/src/components/Books/CreateBookModal.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Books/CreateBookModal.tsx b/web/src/components/Books/CreateBookModal.tsx index b472800c..724835f3 100644 --- a/web/src/components/Books/CreateBookModal.tsx +++ b/web/src/components/Books/CreateBookModal.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Books/DeleteBookModal.scss b/web/src/components/Books/DeleteBookModal.scss index 4b44e711..0b77906a 100644 --- a/web/src/components/Books/DeleteBookModal.scss +++ b/web/src/components/Books/DeleteBookModal.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Books/DeleteBookModal.tsx b/web/src/components/Books/DeleteBookModal.tsx index 95876cd9..6d7880c9 100644 --- a/web/src/components/Books/DeleteBookModal.tsx +++ b/web/src/components/Books/DeleteBookModal.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Books/EmptyList.scss b/web/src/components/Books/EmptyList.scss index 9119ee49..02595ee2 100644 --- a/web/src/components/Books/EmptyList.scss +++ b/web/src/components/Books/EmptyList.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Books/EmptyList.tsx b/web/src/components/Books/EmptyList.tsx index 1fa32a0a..aeb4b599 100644 --- a/web/src/components/Books/EmptyList.tsx +++ b/web/src/components/Books/EmptyList.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Books/HeadData.tsx b/web/src/components/Books/HeadData.tsx index e51b5fb1..6617674f 100644 --- a/web/src/components/Books/HeadData.tsx +++ b/web/src/components/Books/HeadData.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Books/index.tsx b/web/src/components/Books/index.tsx index 25b9326e..509f08d1 100644 --- a/web/src/components/Books/index.tsx +++ b/web/src/components/Books/index.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Auth.scss b/web/src/components/Common/Auth.scss index a47829ca..948c3114 100644 --- a/web/src/components/Common/Auth.scss +++ b/web/src/components/Common/Auth.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Button/Button.scss b/web/src/components/Common/Button/Button.scss index 69a39c89..9a4dce6d 100644 --- a/web/src/components/Common/Button/Button.scss +++ b/web/src/components/Common/Button/Button.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Button/index.tsx b/web/src/components/Common/Button/index.tsx index 11d09d66..a4f731e9 100644 --- a/web/src/components/Common/Button/index.tsx +++ b/web/src/components/Common/Button/index.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Editor/BookSelector/OptionItem.scss b/web/src/components/Common/Editor/BookSelector/OptionItem.scss index 38613fc7..b25ae817 100644 --- a/web/src/components/Common/Editor/BookSelector/OptionItem.scss +++ b/web/src/components/Common/Editor/BookSelector/OptionItem.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Editor/BookSelector/OptionItem.tsx b/web/src/components/Common/Editor/BookSelector/OptionItem.tsx index dc897585..edcc14a2 100644 --- a/web/src/components/Common/Editor/BookSelector/OptionItem.tsx +++ b/web/src/components/Common/Editor/BookSelector/OptionItem.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Editor/BookSelector/index.scss b/web/src/components/Common/Editor/BookSelector/index.scss index fc01c7fb..ae07284c 100644 --- a/web/src/components/Common/Editor/BookSelector/index.scss +++ b/web/src/components/Common/Editor/BookSelector/index.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Editor/BookSelector/index.tsx b/web/src/components/Common/Editor/BookSelector/index.tsx index 0436b12c..8f9ed070 100644 --- a/web/src/components/Common/Editor/BookSelector/index.tsx +++ b/web/src/components/Common/Editor/BookSelector/index.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Editor/Editor.scss b/web/src/components/Common/Editor/Editor.scss index b1f142d0..abf4e170 100644 --- a/web/src/components/Common/Editor/Editor.scss +++ b/web/src/components/Common/Editor/Editor.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Editor/Preview.scss b/web/src/components/Common/Editor/Preview.scss index a2cf036a..59ffc632 100644 --- a/web/src/components/Common/Editor/Preview.scss +++ b/web/src/components/Common/Editor/Preview.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Editor/Preview.tsx b/web/src/components/Common/Editor/Preview.tsx index c7e4d11f..fc59caac 100644 --- a/web/src/components/Common/Editor/Preview.tsx +++ b/web/src/components/Common/Editor/Preview.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Editor/Textarea.scss b/web/src/components/Common/Editor/Textarea.scss index 19a14823..dff44c11 100644 --- a/web/src/components/Common/Editor/Textarea.scss +++ b/web/src/components/Common/Editor/Textarea.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Editor/Textarea.tsx b/web/src/components/Common/Editor/Textarea.tsx index a0f6bbb0..645f2fe2 100644 --- a/web/src/components/Common/Editor/Textarea.tsx +++ b/web/src/components/Common/Editor/Textarea.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Editor/index.tsx b/web/src/components/Common/Editor/index.tsx index fd322d5c..57ef9070 100644 --- a/web/src/components/Common/Editor/index.tsx +++ b/web/src/components/Common/Editor/index.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Flash/Flash.scss b/web/src/components/Common/Flash/Flash.scss index dacc329a..be8d3b27 100644 --- a/web/src/components/Common/Flash/Flash.scss +++ b/web/src/components/Common/Flash/Flash.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Flash/index.tsx b/web/src/components/Common/Flash/index.tsx index 749b6ac9..5ff6fff1 100644 --- a/web/src/components/Common/Flash/index.tsx +++ b/web/src/components/Common/Flash/index.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/ItemActions/ItemActions.scss b/web/src/components/Common/ItemActions/ItemActions.scss index c9c7b801..3d052f6b 100644 --- a/web/src/components/Common/ItemActions/ItemActions.scss +++ b/web/src/components/Common/ItemActions/ItemActions.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/ItemActions/index.tsx b/web/src/components/Common/ItemActions/index.tsx index 90d3c154..3473f647 100644 --- a/web/src/components/Common/ItemActions/index.tsx +++ b/web/src/components/Common/ItemActions/index.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/LegacyFooter.js b/web/src/components/Common/LegacyFooter.js index ac670f36..686e1bb4 100644 --- a/web/src/components/Common/LegacyFooter.js +++ b/web/src/components/Common/LegacyFooter.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Menu/Menu.scss b/web/src/components/Common/Menu/Menu.scss index d5f7abc3..cf0c335c 100644 --- a/web/src/components/Common/Menu/Menu.scss +++ b/web/src/components/Common/Menu/Menu.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Menu/index.tsx b/web/src/components/Common/Menu/index.tsx index 34f8fa38..3eca19e7 100644 --- a/web/src/components/Common/Menu/index.tsx +++ b/web/src/components/Common/Menu/index.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Menu/types.ts b/web/src/components/Common/Menu/types.ts index 389bef38..1dd96441 100644 --- a/web/src/components/Common/Menu/types.ts +++ b/web/src/components/Common/Menu/types.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/MobileMenu.scss b/web/src/components/Common/MobileMenu.scss index c60350bd..720db1fd 100644 --- a/web/src/components/Common/MobileMenu.scss +++ b/web/src/components/Common/MobileMenu.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/MobileMenu.tsx b/web/src/components/Common/MobileMenu.tsx index a943b686..061ebb4c 100644 --- a/web/src/components/Common/MobileMenu.tsx +++ b/web/src/components/Common/MobileMenu.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Modal/Body.tsx b/web/src/components/Common/Modal/Body.tsx index ec3d0c5d..7992db91 100644 --- a/web/src/components/Common/Modal/Body.tsx +++ b/web/src/components/Common/Modal/Body.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Modal/Header.tsx b/web/src/components/Common/Modal/Header.tsx index 443d3874..08721743 100644 --- a/web/src/components/Common/Modal/Header.tsx +++ b/web/src/components/Common/Modal/Header.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Modal/Modal.scss b/web/src/components/Common/Modal/Modal.scss index b007f4b0..2603c8c1 100644 --- a/web/src/components/Common/Modal/Modal.scss +++ b/web/src/components/Common/Modal/Modal.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Modal/ModalBody.scss b/web/src/components/Common/Modal/ModalBody.scss index 9d7b76a7..71efa0fd 100644 --- a/web/src/components/Common/Modal/ModalBody.scss +++ b/web/src/components/Common/Modal/ModalBody.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Modal/ModalHeader.scss b/web/src/components/Common/Modal/ModalHeader.scss index 2cb0e8c7..49bd9b03 100644 --- a/web/src/components/Common/Modal/ModalHeader.scss +++ b/web/src/components/Common/Modal/ModalHeader.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Modal/index.tsx b/web/src/components/Common/Modal/index.tsx index b3aec622..d5253360 100644 --- a/web/src/components/Common/Modal/index.tsx +++ b/web/src/components/Common/Modal/index.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/MultiSelect.scss b/web/src/components/Common/MultiSelect.scss index 90e196d9..29784958 100644 --- a/web/src/components/Common/MultiSelect.scss +++ b/web/src/components/Common/MultiSelect.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/MultiSelect.tsx b/web/src/components/Common/MultiSelect.tsx index 783bd122..397fbde1 100644 --- a/web/src/components/Common/MultiSelect.tsx +++ b/web/src/components/Common/MultiSelect.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/NotFound.tsx b/web/src/components/Common/NotFound.tsx index 4e614955..c32b8daa 100644 --- a/web/src/components/Common/NotFound.tsx +++ b/web/src/components/Common/NotFound.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Note/Content.tsx b/web/src/components/Common/Note/Content.tsx index 1e175424..7650b904 100644 --- a/web/src/components/Common/Note/Content.tsx +++ b/web/src/components/Common/Note/Content.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Note/Footer.tsx b/web/src/components/Common/Note/Footer.tsx index e9cc9bc8..716edc14 100644 --- a/web/src/components/Common/Note/Footer.tsx +++ b/web/src/components/Common/Note/Footer.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Note/Note.scss b/web/src/components/Common/Note/Note.scss index a1ec7ee1..13cf1ec2 100644 --- a/web/src/components/Common/Note/Note.scss +++ b/web/src/components/Common/Note/Note.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Note/Placeholder.scss b/web/src/components/Common/Note/Placeholder.scss index 01c171da..4a83f7ce 100644 --- a/web/src/components/Common/Note/Placeholder.scss +++ b/web/src/components/Common/Note/Placeholder.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Note/Placeholder.tsx b/web/src/components/Common/Note/Placeholder.tsx index e092cfbe..e4d546a6 100644 --- a/web/src/components/Common/Note/Placeholder.tsx +++ b/web/src/components/Common/Note/Placeholder.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Note/index.tsx b/web/src/components/Common/Note/index.tsx index 9c818e67..974b981c 100644 --- a/web/src/components/Common/Note/index.tsx +++ b/web/src/components/Common/Note/index.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/PageToolbar/Paginator/PageLink.tsx b/web/src/components/Common/PageToolbar/Paginator/PageLink.tsx index 63b612a9..11243e2b 100644 --- a/web/src/components/Common/PageToolbar/Paginator/PageLink.tsx +++ b/web/src/components/Common/PageToolbar/Paginator/PageLink.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/PageToolbar/Paginator/Paginator.scss b/web/src/components/Common/PageToolbar/Paginator/Paginator.scss index a1806643..4ae5b03e 100644 --- a/web/src/components/Common/PageToolbar/Paginator/Paginator.scss +++ b/web/src/components/Common/PageToolbar/Paginator/Paginator.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/PageToolbar/Paginator/index.tsx b/web/src/components/Common/PageToolbar/Paginator/index.tsx index b0531d77..3f7199e6 100644 --- a/web/src/components/Common/PageToolbar/Paginator/index.tsx +++ b/web/src/components/Common/PageToolbar/Paginator/index.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/PageToolbar/SelectMenu.scss b/web/src/components/Common/PageToolbar/SelectMenu.scss index 68815bf8..8994ed27 100644 --- a/web/src/components/Common/PageToolbar/SelectMenu.scss +++ b/web/src/components/Common/PageToolbar/SelectMenu.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/PageToolbar/SelectMenu.tsx b/web/src/components/Common/PageToolbar/SelectMenu.tsx index d2b03588..47059449 100644 --- a/web/src/components/Common/PageToolbar/SelectMenu.tsx +++ b/web/src/components/Common/PageToolbar/SelectMenu.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/PageToolbar/index.scss b/web/src/components/Common/PageToolbar/index.scss index 2f403d6d..bcc36353 100644 --- a/web/src/components/Common/PageToolbar/index.scss +++ b/web/src/components/Common/PageToolbar/index.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/PageToolbar/index.tsx b/web/src/components/Common/PageToolbar/index.tsx index 203689cb..1261ebe3 100644 --- a/web/src/components/Common/PageToolbar/index.tsx +++ b/web/src/components/Common/PageToolbar/index.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/PayWall.scss b/web/src/components/Common/PayWall.scss index 5ca44378..a5d250ba 100644 --- a/web/src/components/Common/PayWall.scss +++ b/web/src/components/Common/PayWall.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/PayWall.tsx b/web/src/components/Common/PayWall.tsx index c34b04d3..bbec1213 100644 --- a/web/src/components/Common/PayWall.tsx +++ b/web/src/components/Common/PayWall.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Popover/Popover.scss b/web/src/components/Common/Popover/Popover.scss index 5c81c3b7..1e253350 100644 --- a/web/src/components/Common/Popover/Popover.scss +++ b/web/src/components/Common/Popover/Popover.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Popover/PopoverContent.scss b/web/src/components/Common/Popover/PopoverContent.scss index d95672e2..978328c9 100644 --- a/web/src/components/Common/Popover/PopoverContent.scss +++ b/web/src/components/Common/Popover/PopoverContent.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Popover/PopoverContent.tsx b/web/src/components/Common/Popover/PopoverContent.tsx index 841f70a7..74e9c3a1 100644 --- a/web/src/components/Common/Popover/PopoverContent.tsx +++ b/web/src/components/Common/Popover/PopoverContent.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Popover/index.tsx b/web/src/components/Common/Popover/index.tsx index 7c3dbb56..f959b078 100644 --- a/web/src/components/Common/Popover/index.tsx +++ b/web/src/components/Common/Popover/index.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Popover/types.ts b/web/src/components/Common/Popover/types.ts index 229ada76..e933ce20 100644 --- a/web/src/components/Common/Popover/types.ts +++ b/web/src/components/Common/Popover/types.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/SearchInput/Actions.tsx b/web/src/components/Common/SearchInput/Actions.tsx index ffe9babc..9fe6fb79 100644 --- a/web/src/components/Common/SearchInput/Actions.tsx +++ b/web/src/components/Common/SearchInput/Actions.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/SearchInput/SearchInput.scss b/web/src/components/Common/SearchInput/SearchInput.scss index a9ab2be3..1a85f54f 100644 --- a/web/src/components/Common/SearchInput/SearchInput.scss +++ b/web/src/components/Common/SearchInput/SearchInput.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/SearchInput/index.tsx b/web/src/components/Common/SearchInput/index.tsx index 7c5bbb85..a8030812 100644 --- a/web/src/components/Common/SearchInput/index.tsx +++ b/web/src/components/Common/SearchInput/index.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/SearchableMenu/Item.tsx b/web/src/components/Common/SearchableMenu/Item.tsx index 7c436364..0c42cac1 100644 --- a/web/src/components/Common/SearchableMenu/Item.tsx +++ b/web/src/components/Common/SearchableMenu/Item.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/SearchableMenu/Result.tsx b/web/src/components/Common/SearchableMenu/Result.tsx index 05a21fdb..bfbeccdd 100644 --- a/web/src/components/Common/SearchableMenu/Result.tsx +++ b/web/src/components/Common/SearchableMenu/Result.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/SearchableMenu/SearchableMenu.scss b/web/src/components/Common/SearchableMenu/SearchableMenu.scss index b16df6db..a6166539 100644 --- a/web/src/components/Common/SearchableMenu/SearchableMenu.scss +++ b/web/src/components/Common/SearchableMenu/SearchableMenu.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/SearchableMenu/index.tsx b/web/src/components/Common/SearchableMenu/index.tsx index aa82faa2..587ea260 100644 --- a/web/src/components/Common/SearchableMenu/index.tsx +++ b/web/src/components/Common/SearchableMenu/index.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Sidebar/SettingsSidebar/SettingsSidebar.module.scss b/web/src/components/Common/Sidebar/SettingsSidebar/SettingsSidebar.module.scss index ca4a5d96..5a55bfe8 100644 --- a/web/src/components/Common/Sidebar/SettingsSidebar/SettingsSidebar.module.scss +++ b/web/src/components/Common/Sidebar/SettingsSidebar/SettingsSidebar.module.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Sidebar/SettingsSidebar/index.js b/web/src/components/Common/Sidebar/SettingsSidebar/index.js index 4abce936..29024864 100644 --- a/web/src/components/Common/Sidebar/SettingsSidebar/index.js +++ b/web/src/components/Common/Sidebar/SettingsSidebar/index.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Sidebar/Sidebar.module.scss b/web/src/components/Common/Sidebar/Sidebar.module.scss index 991be720..c1609497 100644 --- a/web/src/components/Common/Sidebar/Sidebar.module.scss +++ b/web/src/components/Common/Sidebar/Sidebar.module.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/SidebarToggle.js b/web/src/components/Common/SidebarToggle.js index ee707594..5cc0930e 100644 --- a/web/src/components/Common/SidebarToggle.js +++ b/web/src/components/Common/SidebarToggle.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/SidebarToggle.module.scss b/web/src/components/Common/SidebarToggle.module.scss index 2248b4e2..59580596 100644 --- a/web/src/components/Common/SidebarToggle.module.scss +++ b/web/src/components/Common/SidebarToggle.module.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/SystemMessage.scss b/web/src/components/Common/SystemMessage.scss index c766b2df..375b8a65 100644 --- a/web/src/components/Common/SystemMessage.scss +++ b/web/src/components/Common/SystemMessage.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/SystemMessage.tsx b/web/src/components/Common/SystemMessage.tsx index 6760fcb9..4e164564 100644 --- a/web/src/components/Common/SystemMessage.tsx +++ b/web/src/components/Common/SystemMessage.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Time.scss b/web/src/components/Common/Time.scss index 8dbb5e7a..bb708657 100644 --- a/web/src/components/Common/Time.scss +++ b/web/src/components/Common/Time.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Time.tsx b/web/src/components/Common/Time.tsx index f0458ecb..f861813e 100644 --- a/web/src/components/Common/Time.tsx +++ b/web/src/components/Common/Time.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Toggle.scss b/web/src/components/Common/Toggle.scss index 82eec8c6..8341e5e6 100644 --- a/web/src/components/Common/Toggle.scss +++ b/web/src/components/Common/Toggle.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Toggle.tsx b/web/src/components/Common/Toggle.tsx index 499456e1..5355a07d 100644 --- a/web/src/components/Common/Toggle.tsx +++ b/web/src/components/Common/Toggle.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Tooltip/Overlay.tsx b/web/src/components/Common/Tooltip/Overlay.tsx index a3136561..ff300889 100644 --- a/web/src/components/Common/Tooltip/Overlay.tsx +++ b/web/src/components/Common/Tooltip/Overlay.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Tooltip/Tooltip.scss b/web/src/components/Common/Tooltip/Tooltip.scss index 52f5bc44..4002d5be 100644 --- a/web/src/components/Common/Tooltip/Tooltip.scss +++ b/web/src/components/Common/Tooltip/Tooltip.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Common/Tooltip/index.tsx b/web/src/components/Common/Tooltip/index.tsx index 03b946c0..4b577522 100644 --- a/web/src/components/Common/Tooltip/index.tsx +++ b/web/src/components/Common/Tooltip/index.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Edit/Content.tsx b/web/src/components/Edit/Content.tsx index b9b10c51..fe867139 100644 --- a/web/src/components/Edit/Content.tsx +++ b/web/src/components/Edit/Content.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Edit/index.tsx b/web/src/components/Edit/index.tsx index 15fa7174..6ada7ca6 100644 --- a/web/src/components/Edit/index.tsx +++ b/web/src/components/Edit/index.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/EmailPreference/EmailPreference.scss b/web/src/components/EmailPreference/EmailPreference.scss index 15df58d0..da45c583 100644 --- a/web/src/components/EmailPreference/EmailPreference.scss +++ b/web/src/components/EmailPreference/EmailPreference.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/EmailPreference/index.tsx b/web/src/components/EmailPreference/index.tsx index baedec99..47606104 100644 --- a/web/src/components/EmailPreference/index.tsx +++ b/web/src/components/EmailPreference/index.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Header/AccountMenu.scss b/web/src/components/Header/AccountMenu.scss index 7f4104ef..70ead6f2 100644 --- a/web/src/components/Header/AccountMenu.scss +++ b/web/src/components/Header/AccountMenu.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Header/AccountMenu.tsx b/web/src/components/Header/AccountMenu.tsx index e8c35dee..e27979c6 100644 --- a/web/src/components/Header/AccountMenu.tsx +++ b/web/src/components/Header/AccountMenu.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Header/DemoHeader.js b/web/src/components/Header/DemoHeader.js index 97410fd9..c6912077 100644 --- a/web/src/components/Header/DemoHeader.js +++ b/web/src/components/Header/DemoHeader.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Header/DemoHeader.module.scss b/web/src/components/Header/DemoHeader.module.scss index 8303c60a..8a536cde 100644 --- a/web/src/components/Header/DemoHeader.module.scss +++ b/web/src/components/Header/DemoHeader.module.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Header/Nav/Item.scss b/web/src/components/Header/Nav/Item.scss index 3c38a1ad..fa592e39 100644 --- a/web/src/components/Header/Nav/Item.scss +++ b/web/src/components/Header/Nav/Item.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Header/Nav/Item.tsx b/web/src/components/Header/Nav/Item.tsx index 9b9993e0..37caa3d7 100644 --- a/web/src/components/Header/Nav/Item.tsx +++ b/web/src/components/Header/Nav/Item.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Header/Nav/Nav.scss b/web/src/components/Header/Nav/Nav.scss index b69d35d7..636d18eb 100644 --- a/web/src/components/Header/Nav/Nav.scss +++ b/web/src/components/Header/Nav/Nav.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Header/Nav/index.tsx b/web/src/components/Header/Nav/index.tsx index 76c2a740..50a81f5f 100644 --- a/web/src/components/Header/Nav/index.tsx +++ b/web/src/components/Header/Nav/index.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Header/Normal.scss b/web/src/components/Header/Normal.scss index 10a8c9cf..e94b6ed1 100644 --- a/web/src/components/Header/Normal.scss +++ b/web/src/components/Header/Normal.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Header/Normal.tsx b/web/src/components/Header/Normal.tsx index 8e669572..aa04fbca 100644 --- a/web/src/components/Header/Normal.tsx +++ b/web/src/components/Header/Normal.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Header/Note/Guest.scss b/web/src/components/Header/Note/Guest.scss index 32bdea1e..cdac39d1 100644 --- a/web/src/components/Header/Note/Guest.scss +++ b/web/src/components/Header/Note/Guest.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Header/Note/Guest.tsx b/web/src/components/Header/Note/Guest.tsx index 7deeedf0..f5b9f054 100644 --- a/web/src/components/Header/Note/Guest.tsx +++ b/web/src/components/Header/Note/Guest.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Header/Note/Placeholder.scss b/web/src/components/Header/Note/Placeholder.scss index 48bd6fe8..fc07a6af 100644 --- a/web/src/components/Header/Note/Placeholder.scss +++ b/web/src/components/Header/Note/Placeholder.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Header/Note/Placeholder.tsx b/web/src/components/Header/Note/Placeholder.tsx index bc3e9cb6..85e5c67c 100644 --- a/web/src/components/Header/Note/Placeholder.tsx +++ b/web/src/components/Header/Note/Placeholder.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Header/Note/index.scss b/web/src/components/Header/Note/index.scss index 51508f84..5b448efb 100644 --- a/web/src/components/Header/Note/index.scss +++ b/web/src/components/Header/Note/index.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Header/Note/index.tsx b/web/src/components/Header/Note/index.tsx index 4f5cbade..f59facff 100644 --- a/web/src/components/Header/Note/index.tsx +++ b/web/src/components/Header/Note/index.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Header/SearchBar/AdvancedPanel/AdvancedPanel.scss b/web/src/components/Header/SearchBar/AdvancedPanel/AdvancedPanel.scss index c27245f4..1395e6e6 100644 --- a/web/src/components/Header/SearchBar/AdvancedPanel/AdvancedPanel.scss +++ b/web/src/components/Header/SearchBar/AdvancedPanel/AdvancedPanel.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Header/SearchBar/AdvancedPanel/BookSearch.tsx b/web/src/components/Header/SearchBar/AdvancedPanel/BookSearch.tsx index 54137007..bbb8fd98 100644 --- a/web/src/components/Header/SearchBar/AdvancedPanel/BookSearch.tsx +++ b/web/src/components/Header/SearchBar/AdvancedPanel/BookSearch.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Header/SearchBar/AdvancedPanel/WordsSearch.tsx b/web/src/components/Header/SearchBar/AdvancedPanel/WordsSearch.tsx index a940edbf..39f562aa 100644 --- a/web/src/components/Header/SearchBar/AdvancedPanel/WordsSearch.tsx +++ b/web/src/components/Header/SearchBar/AdvancedPanel/WordsSearch.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Header/SearchBar/AdvancedPanel/index.tsx b/web/src/components/Header/SearchBar/AdvancedPanel/index.tsx index fb50ec1a..dab9920d 100644 --- a/web/src/components/Header/SearchBar/AdvancedPanel/index.tsx +++ b/web/src/components/Header/SearchBar/AdvancedPanel/index.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Header/SearchBar/SearchBar.scss b/web/src/components/Header/SearchBar/SearchBar.scss index 5e8fba96..9fce6e71 100644 --- a/web/src/components/Header/SearchBar/SearchBar.scss +++ b/web/src/components/Header/SearchBar/SearchBar.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Header/SearchBar/index.tsx b/web/src/components/Header/SearchBar/index.tsx index 95607817..7380006b 100644 --- a/web/src/components/Header/SearchBar/index.tsx +++ b/web/src/components/Header/SearchBar/index.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Home/HeadData.tsx b/web/src/components/Home/HeadData.tsx index 2260d182..6194efad 100644 --- a/web/src/components/Home/HeadData.tsx +++ b/web/src/components/Home/HeadData.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Home/Home.scss b/web/src/components/Home/Home.scss index 517c050f..5153a891 100644 --- a/web/src/components/Home/Home.scss +++ b/web/src/components/Home/Home.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Home/NoteGroup/Header.scss b/web/src/components/Home/NoteGroup/Header.scss index 59a263ab..63821e30 100644 --- a/web/src/components/Home/NoteGroup/Header.scss +++ b/web/src/components/Home/NoteGroup/Header.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Home/NoteGroup/Header.tsx b/web/src/components/Home/NoteGroup/Header.tsx index f531ba5a..f0b8c5e5 100644 --- a/web/src/components/Home/NoteGroup/Header.tsx +++ b/web/src/components/Home/NoteGroup/Header.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Home/NoteGroup/List.scss b/web/src/components/Home/NoteGroup/List.scss index d4646901..dc0f34fb 100644 --- a/web/src/components/Home/NoteGroup/List.scss +++ b/web/src/components/Home/NoteGroup/List.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Home/NoteGroup/List.tsx b/web/src/components/Home/NoteGroup/List.tsx index 0c378792..6eca65f5 100644 --- a/web/src/components/Home/NoteGroup/List.tsx +++ b/web/src/components/Home/NoteGroup/List.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Home/NoteGroup/NoteGroup.scss b/web/src/components/Home/NoteGroup/NoteGroup.scss index 1d70bcc7..344357b9 100644 --- a/web/src/components/Home/NoteGroup/NoteGroup.scss +++ b/web/src/components/Home/NoteGroup/NoteGroup.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Home/NoteGroup/NoteItem.scss b/web/src/components/Home/NoteGroup/NoteItem.scss index cf2d3f22..29cc6aa6 100644 --- a/web/src/components/Home/NoteGroup/NoteItem.scss +++ b/web/src/components/Home/NoteGroup/NoteItem.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Home/NoteGroup/NoteItem.tsx b/web/src/components/Home/NoteGroup/NoteItem.tsx index 52eb221a..801c1c2d 100644 --- a/web/src/components/Home/NoteGroup/NoteItem.tsx +++ b/web/src/components/Home/NoteGroup/NoteItem.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Home/NoteGroup/Placeholder.scss b/web/src/components/Home/NoteGroup/Placeholder.scss index 6a007f56..c1c7f2b7 100644 --- a/web/src/components/Home/NoteGroup/Placeholder.scss +++ b/web/src/components/Home/NoteGroup/Placeholder.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Home/NoteGroup/Placeholder.tsx b/web/src/components/Home/NoteGroup/Placeholder.tsx index c70f8a57..0b2d78c0 100644 --- a/web/src/components/Home/NoteGroup/Placeholder.tsx +++ b/web/src/components/Home/NoteGroup/Placeholder.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Home/NoteGroup/index.tsx b/web/src/components/Home/NoteGroup/index.tsx index 603c36c2..f5056680 100644 --- a/web/src/components/Home/NoteGroup/index.tsx +++ b/web/src/components/Home/NoteGroup/index.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Home/index.tsx b/web/src/components/Home/index.tsx index a1b5a7e9..ca2ede70 100644 --- a/web/src/components/Home/index.tsx +++ b/web/src/components/Home/index.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Icons/Arrow.js b/web/src/components/Icons/Arrow.js index 5d15b22d..0c6c200e 100644 --- a/web/src/components/Icons/Arrow.js +++ b/web/src/components/Icons/Arrow.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Icons/Atom.js b/web/src/components/Icons/Atom.js index adba58b2..b484841c 100644 --- a/web/src/components/Icons/Atom.js +++ b/web/src/components/Icons/Atom.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Icons/Book.tsx b/web/src/components/Icons/Book.tsx index 89138569..80fc686d 100644 --- a/web/src/components/Icons/Book.tsx +++ b/web/src/components/Icons/Book.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Icons/BookPlus.tsx b/web/src/components/Icons/BookPlus.tsx index f98d1b63..40f8f655 100644 --- a/web/src/components/Icons/BookPlus.tsx +++ b/web/src/components/Icons/BookPlus.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Icons/Books.js b/web/src/components/Icons/Books.js index 37a9f151..4b01a928 100644 --- a/web/src/components/Icons/Books.js +++ b/web/src/components/Icons/Books.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Icons/Box.tsx b/web/src/components/Icons/Box.tsx index 6a2d506c..bcc8f2ac 100644 --- a/web/src/components/Icons/Box.tsx +++ b/web/src/components/Icons/Box.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Icons/Caret.tsx b/web/src/components/Icons/Caret.tsx index 729e9605..8cc26fb6 100644 --- a/web/src/components/Icons/Caret.tsx +++ b/web/src/components/Icons/Caret.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Icons/CaretSolid.tsx b/web/src/components/Icons/CaretSolid.tsx index ef2a2ad6..cdc86c7a 100644 --- a/web/src/components/Icons/CaretSolid.tsx +++ b/web/src/components/Icons/CaretSolid.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Icons/Check.js b/web/src/components/Icons/Check.js index 5482a8dc..77cd7512 100644 --- a/web/src/components/Icons/Check.js +++ b/web/src/components/Icons/Check.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Icons/CheckCircle.tsx b/web/src/components/Icons/CheckCircle.tsx index 99c0d0b4..8749bc5c 100644 --- a/web/src/components/Icons/CheckCircle.tsx +++ b/web/src/components/Icons/CheckCircle.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Icons/Chrome.js b/web/src/components/Icons/Chrome.js index ee73be3f..eb5d706a 100644 --- a/web/src/components/Icons/Chrome.js +++ b/web/src/components/Icons/Chrome.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Icons/Close.tsx b/web/src/components/Icons/Close.tsx index b940b84d..b21ec976 100644 --- a/web/src/components/Icons/Close.tsx +++ b/web/src/components/Icons/Close.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Icons/Cloud.js b/web/src/components/Icons/Cloud.js index 28a70333..4229a56d 100644 --- a/web/src/components/Icons/Cloud.js +++ b/web/src/components/Icons/Cloud.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Icons/CreditCard.js b/web/src/components/Icons/CreditCard.js index 3e94e6e7..51bcbdf6 100644 --- a/web/src/components/Icons/CreditCard.js +++ b/web/src/components/Icons/CreditCard.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Icons/Dashboard.tsx b/web/src/components/Icons/Dashboard.tsx index c5e22d1f..cd73b748 100644 --- a/web/src/components/Icons/Dashboard.tsx +++ b/web/src/components/Icons/Dashboard.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Icons/Dots.tsx b/web/src/components/Icons/Dots.tsx index 8e723df8..b884bd50 100644 --- a/web/src/components/Icons/Dots.tsx +++ b/web/src/components/Icons/Dots.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Icons/Email.js b/web/src/components/Icons/Email.js index 4cf375d0..c35685d6 100644 --- a/web/src/components/Icons/Email.js +++ b/web/src/components/Icons/Email.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Icons/Firefox.js b/web/src/components/Icons/Firefox.js index 6c1ea008..d5b2b4b0 100644 --- a/web/src/components/Icons/Firefox.js +++ b/web/src/components/Icons/Firefox.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Icons/Globe.tsx b/web/src/components/Icons/Globe.tsx index b5784753..679d324f 100644 --- a/web/src/components/Icons/Globe.tsx +++ b/web/src/components/Icons/Globe.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Icons/Home.tsx b/web/src/components/Icons/Home.tsx index 73e88894..faad0738 100644 --- a/web/src/components/Icons/Home.tsx +++ b/web/src/components/Icons/Home.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Icons/Link.js b/web/src/components/Icons/Link.js index 6a25b041..bcea64b8 100644 --- a/web/src/components/Icons/Link.js +++ b/web/src/components/Icons/Link.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Icons/Loading.js b/web/src/components/Icons/Loading.js index a5be9a97..17bef543 100644 --- a/web/src/components/Icons/Loading.js +++ b/web/src/components/Icons/Loading.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Icons/Lock.js b/web/src/components/Icons/Lock.js index 78c839e2..53f61630 100644 --- a/web/src/components/Icons/Lock.js +++ b/web/src/components/Icons/Lock.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Icons/Logo.tsx b/web/src/components/Icons/Logo.tsx index 96baf588..119c57bb 100644 --- a/web/src/components/Icons/Logo.tsx +++ b/web/src/components/Icons/Logo.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Icons/LogoWithText.tsx b/web/src/components/Icons/LogoWithText.tsx index 5e4c8d39..b7840eba 100644 --- a/web/src/components/Icons/LogoWithText.tsx +++ b/web/src/components/Icons/LogoWithText.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Icons/Menu.js b/web/src/components/Icons/Menu.js index 0889b3d2..deddce5a 100644 --- a/web/src/components/Icons/Menu.js +++ b/web/src/components/Icons/Menu.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Icons/Note.tsx b/web/src/components/Icons/Note.tsx index bf53ec71..6a6baa5b 100644 --- a/web/src/components/Icons/Note.tsx +++ b/web/src/components/Icons/Note.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Icons/Search.tsx b/web/src/components/Icons/Search.tsx index db603bf7..ba1460ff 100644 --- a/web/src/components/Icons/Search.tsx +++ b/web/src/components/Icons/Search.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Icons/Server.tsx b/web/src/components/Icons/Server.tsx index 8b788dd0..48ec19a9 100644 --- a/web/src/components/Icons/Server.tsx +++ b/web/src/components/Icons/Server.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Icons/Spinner.js b/web/src/components/Icons/Spinner.js index 50ea3aba..b08e1369 100644 --- a/web/src/components/Icons/Spinner.js +++ b/web/src/components/Icons/Spinner.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Icons/Terminal.js b/web/src/components/Icons/Terminal.js index 16b9e894..90f942c5 100644 --- a/web/src/components/Icons/Terminal.js +++ b/web/src/components/Icons/Terminal.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Icons/Trash.tsx b/web/src/components/Icons/Trash.tsx index f22926d2..70c5c7a3 100644 --- a/web/src/components/Icons/Trash.tsx +++ b/web/src/components/Icons/Trash.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Icons/Unlink.js b/web/src/components/Icons/Unlink.js index b70e22f3..2041e95f 100644 --- a/web/src/components/Icons/Unlink.js +++ b/web/src/components/Icons/Unlink.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Icons/User.tsx b/web/src/components/Icons/User.tsx index 32ba37a8..9c465bc9 100644 --- a/web/src/components/Icons/User.tsx +++ b/web/src/components/Icons/User.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Icons/Users.js b/web/src/components/Icons/Users.js index 214e1789..6006ab22 100644 --- a/web/src/components/Icons/Users.js +++ b/web/src/components/Icons/Users.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Icons/Web.js b/web/src/components/Icons/Web.js index d4a53f88..b220588f 100644 --- a/web/src/components/Icons/Web.js +++ b/web/src/components/Icons/Web.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Icons/types.ts b/web/src/components/Icons/types.ts index 4241f9a7..5b863c4d 100644 --- a/web/src/components/Icons/types.ts +++ b/web/src/components/Icons/types.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Join/JoinForm.tsx b/web/src/components/Join/JoinForm.tsx index 942b54d9..758e4d45 100644 --- a/web/src/components/Join/JoinForm.tsx +++ b/web/src/components/Join/JoinForm.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Join/index.tsx b/web/src/components/Join/index.tsx index 719abbcf..7280770a 100644 --- a/web/src/components/Join/index.tsx +++ b/web/src/components/Join/index.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Login/LoginForm.tsx b/web/src/components/Login/LoginForm.tsx index 8a95da26..5239d8cb 100644 --- a/web/src/components/Login/LoginForm.tsx +++ b/web/src/components/Login/LoginForm.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Login/index.tsx b/web/src/components/Login/index.tsx index ce3d7bcc..cd6fc906 100644 --- a/web/src/components/Login/index.tsx +++ b/web/src/components/Login/index.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/New/Content.tsx b/web/src/components/New/Content.tsx index 1445fc06..a33cc496 100644 --- a/web/src/components/New/Content.tsx +++ b/web/src/components/New/Content.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/New/New.scss b/web/src/components/New/New.scss index 04f55fff..ecc593ee 100644 --- a/web/src/components/New/New.scss +++ b/web/src/components/New/New.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/New/index.tsx b/web/src/components/New/index.tsx index 80dd5e0e..ada7cbad 100644 --- a/web/src/components/New/index.tsx +++ b/web/src/components/New/index.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Note/DeleteModal.scss b/web/src/components/Note/DeleteModal.scss index 4d3cd9fa..b373f159 100644 --- a/web/src/components/Note/DeleteModal.scss +++ b/web/src/components/Note/DeleteModal.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Note/DeleteModal.tsx b/web/src/components/Note/DeleteModal.tsx index 0204a4d4..b5356cd0 100644 --- a/web/src/components/Note/DeleteModal.tsx +++ b/web/src/components/Note/DeleteModal.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Note/FooterActions.scss b/web/src/components/Note/FooterActions.scss index 81af3963..bac68f86 100644 --- a/web/src/components/Note/FooterActions.scss +++ b/web/src/components/Note/FooterActions.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Note/FooterActions.tsx b/web/src/components/Note/FooterActions.tsx index 7ae7c0fb..b886faca 100644 --- a/web/src/components/Note/FooterActions.tsx +++ b/web/src/components/Note/FooterActions.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Note/Header.scss b/web/src/components/Note/Header.scss index e800e878..8d396f04 100644 --- a/web/src/components/Note/Header.scss +++ b/web/src/components/Note/Header.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Note/Header.tsx b/web/src/components/Note/Header.tsx index 8a0a5ab9..b8ec3ee4 100644 --- a/web/src/components/Note/Header.tsx +++ b/web/src/components/Note/Header.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Note/HeaderData.tsx b/web/src/components/Note/HeaderData.tsx index 9ff3ec56..84fad232 100644 --- a/web/src/components/Note/HeaderData.tsx +++ b/web/src/components/Note/HeaderData.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Note/HeaderRight.tsx b/web/src/components/Note/HeaderRight.tsx index 1eae6c23..679493c6 100644 --- a/web/src/components/Note/HeaderRight.tsx +++ b/web/src/components/Note/HeaderRight.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Note/ShareModal/CopyButton.tsx b/web/src/components/Note/ShareModal/CopyButton.tsx index dc5e09b3..116f120d 100644 --- a/web/src/components/Note/ShareModal/CopyButton.tsx +++ b/web/src/components/Note/ShareModal/CopyButton.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Note/ShareModal/ShareModal.scss b/web/src/components/Note/ShareModal/ShareModal.scss index 3243bf19..7b2ed708 100644 --- a/web/src/components/Note/ShareModal/ShareModal.scss +++ b/web/src/components/Note/ShareModal/ShareModal.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Note/ShareModal/index.tsx b/web/src/components/Note/ShareModal/index.tsx index c4001acb..8ed37581 100644 --- a/web/src/components/Note/ShareModal/index.tsx +++ b/web/src/components/Note/ShareModal/index.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Note/index.scss b/web/src/components/Note/index.scss index 0c11ec33..99a4625f 100644 --- a/web/src/components/Note/index.scss +++ b/web/src/components/Note/index.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Note/index.tsx b/web/src/components/Note/index.tsx index ca05f5ed..7ca56324 100644 --- a/web/src/components/Note/index.tsx +++ b/web/src/components/Note/index.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/PasswordReset/Confirm/Form.tsx b/web/src/components/PasswordReset/Confirm/Form.tsx index 1efa2883..54eb9b44 100644 --- a/web/src/components/PasswordReset/Confirm/Form.tsx +++ b/web/src/components/PasswordReset/Confirm/Form.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/PasswordReset/Confirm/index.tsx b/web/src/components/PasswordReset/Confirm/index.tsx index 1f33b7b5..af576743 100644 --- a/web/src/components/PasswordReset/Confirm/index.tsx +++ b/web/src/components/PasswordReset/Confirm/index.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/PasswordReset/Request/Form.scss b/web/src/components/PasswordReset/Request/Form.scss index eeadf901..18f9f4ff 100644 --- a/web/src/components/PasswordReset/Request/Form.scss +++ b/web/src/components/PasswordReset/Request/Form.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/PasswordReset/Request/Form.tsx b/web/src/components/PasswordReset/Request/Form.tsx index fa5ff06a..166d2f1e 100644 --- a/web/src/components/PasswordReset/Request/Form.tsx +++ b/web/src/components/PasswordReset/Request/Form.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/PasswordReset/Request/Request.scss b/web/src/components/PasswordReset/Request/Request.scss index 04b77666..636cad90 100644 --- a/web/src/components/PasswordReset/Request/Request.scss +++ b/web/src/components/PasswordReset/Request/Request.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/PasswordReset/Request/index.tsx b/web/src/components/PasswordReset/Request/index.tsx index 48f80273..7cc85265 100644 --- a/web/src/components/PasswordReset/Request/index.tsx +++ b/web/src/components/PasswordReset/Request/index.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Settings/About/index.tsx b/web/src/components/Settings/About/index.tsx index 0f289423..74f014c7 100644 --- a/web/src/components/Settings/About/index.tsx +++ b/web/src/components/Settings/About/index.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Settings/Account/EmailModal.tsx b/web/src/components/Settings/Account/EmailModal.tsx index 7a2cb475..443820d3 100644 --- a/web/src/components/Settings/Account/EmailModal.tsx +++ b/web/src/components/Settings/Account/EmailModal.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Settings/Account/EmailVerificationRow.tsx b/web/src/components/Settings/Account/EmailVerificationRow.tsx index ba22c1f5..40286c6a 100644 --- a/web/src/components/Settings/Account/EmailVerificationRow.tsx +++ b/web/src/components/Settings/Account/EmailVerificationRow.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Settings/Account/PasswordModal.tsx b/web/src/components/Settings/Account/PasswordModal.tsx index e1b24408..6d8bc1af 100644 --- a/web/src/components/Settings/Account/PasswordModal.tsx +++ b/web/src/components/Settings/Account/PasswordModal.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Settings/Account/index.tsx b/web/src/components/Settings/Account/index.tsx index efaf6ba5..06407fda 100644 --- a/web/src/components/Settings/Account/index.tsx +++ b/web/src/components/Settings/Account/index.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Settings/Notifications/Form.scss b/web/src/components/Settings/Notifications/Form.scss index 3d56d1bc..6de303ca 100644 --- a/web/src/components/Settings/Notifications/Form.scss +++ b/web/src/components/Settings/Notifications/Form.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Settings/Notifications/Form.tsx b/web/src/components/Settings/Notifications/Form.tsx index 110a4f3d..18395015 100644 --- a/web/src/components/Settings/Notifications/Form.tsx +++ b/web/src/components/Settings/Notifications/Form.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Settings/Notifications/Notifications.scss b/web/src/components/Settings/Notifications/Notifications.scss index 202b2b43..7b74ee4d 100644 --- a/web/src/components/Settings/Notifications/Notifications.scss +++ b/web/src/components/Settings/Notifications/Notifications.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Settings/Notifications/index.tsx b/web/src/components/Settings/Notifications/index.tsx index dde37860..74925942 100644 --- a/web/src/components/Settings/Notifications/index.tsx +++ b/web/src/components/Settings/Notifications/index.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Settings/SettingRow.scss b/web/src/components/Settings/SettingRow.scss index e904aaf3..fb0cbaef 100644 --- a/web/src/components/Settings/SettingRow.scss +++ b/web/src/components/Settings/SettingRow.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Settings/SettingRow.tsx b/web/src/components/Settings/SettingRow.tsx index 84095617..fb121c78 100644 --- a/web/src/components/Settings/SettingRow.tsx +++ b/web/src/components/Settings/SettingRow.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Settings/Settings.scss b/web/src/components/Settings/Settings.scss index 6cafaaba..e62fdcaa 100644 --- a/web/src/components/Settings/Settings.scss +++ b/web/src/components/Settings/Settings.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Settings/Sidebar.scss b/web/src/components/Settings/Sidebar.scss index 0848f81c..fed80693 100644 --- a/web/src/components/Settings/Sidebar.scss +++ b/web/src/components/Settings/Sidebar.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Settings/Sidebar.tsx b/web/src/components/Settings/Sidebar.tsx index 07811a01..4835d5dc 100644 --- a/web/src/components/Settings/Sidebar.tsx +++ b/web/src/components/Settings/Sidebar.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Settings/index.tsx b/web/src/components/Settings/index.tsx index 5837fffb..13d21d9a 100644 --- a/web/src/components/Settings/index.tsx +++ b/web/src/components/Settings/index.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Splash/Splash.module.scss b/web/src/components/Splash/Splash.module.scss index 4a42b741..4083215e 100644 --- a/web/src/components/Splash/Splash.module.scss +++ b/web/src/components/Splash/Splash.module.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/Splash/index.tsx b/web/src/components/Splash/index.tsx index 88841d7a..fd9fe67d 100644 --- a/web/src/components/Splash/index.tsx +++ b/web/src/components/Splash/index.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/TabBar/Item.scss b/web/src/components/TabBar/Item.scss index 7ba11c8a..a9ec245c 100644 --- a/web/src/components/TabBar/Item.scss +++ b/web/src/components/TabBar/Item.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/TabBar/Item.tsx b/web/src/components/TabBar/Item.tsx index bf7f7710..07f63095 100644 --- a/web/src/components/TabBar/Item.tsx +++ b/web/src/components/TabBar/Item.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/TabBar/TabBar.scss b/web/src/components/TabBar/TabBar.scss index 1c39ddd9..297013f2 100644 --- a/web/src/components/TabBar/TabBar.scss +++ b/web/src/components/TabBar/TabBar.scss @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/TabBar/index.tsx b/web/src/components/TabBar/index.tsx index be9f9cc9..b722df0b 100644 --- a/web/src/components/TabBar/index.tsx +++ b/web/src/components/TabBar/index.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/components/VerifyEmail/index.tsx b/web/src/components/VerifyEmail/index.tsx index c81c891e..d90795ce 100644 --- a/web/src/components/VerifyEmail/index.tsx +++ b/web/src/components/VerifyEmail/index.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/helpers/accessibility.ts b/web/src/helpers/accessibility.ts index 21d0343e..62cd20d6 100644 --- a/web/src/helpers/accessibility.ts +++ b/web/src/helpers/accessibility.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/helpers/browser.js b/web/src/helpers/browser.js index bae9cc8f..b75beee6 100644 --- a/web/src/helpers/browser.js +++ b/web/src/helpers/browser.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/helpers/error.js b/web/src/helpers/error.js index dba185e3..2e084b14 100644 --- a/web/src/helpers/error.js +++ b/web/src/helpers/error.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/helpers/markdown.ts b/web/src/helpers/markdown.ts index 408c11ae..0047f907 100644 --- a/web/src/helpers/markdown.ts +++ b/web/src/helpers/markdown.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/helpers/time/format.spec.ts b/web/src/helpers/time/format.spec.ts index 88a874a3..c9473064 100644 --- a/web/src/helpers/time/format.spec.ts +++ b/web/src/helpers/time/format.spec.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/helpers/time/format.ts b/web/src/helpers/time/format.ts index 720ca7de..df806d58 100644 --- a/web/src/helpers/time/format.ts +++ b/web/src/helpers/time/format.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/helpers/time/index.spec.ts b/web/src/helpers/time/index.spec.ts index 34da035f..4a1e292d 100644 --- a/web/src/helpers/time/index.spec.ts +++ b/web/src/helpers/time/index.spec.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/helpers/time/index.ts b/web/src/helpers/time/index.ts index 64ad12d0..a10e810a 100644 --- a/web/src/helpers/time/index.ts +++ b/web/src/helpers/time/index.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/helpers/user.js b/web/src/helpers/user.js index c7a42bce..c0f133f3 100644 --- a/web/src/helpers/user.js +++ b/web/src/helpers/user.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/hocs/guestOnly.tsx b/web/src/hocs/guestOnly.tsx index 251ec0c5..e67ff4f3 100644 --- a/web/src/hocs/guestOnly.tsx +++ b/web/src/hocs/guestOnly.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/hocs/scrollTop.tsx b/web/src/hocs/scrollTop.tsx index 79d83015..2d4ccce0 100644 --- a/web/src/hocs/scrollTop.tsx +++ b/web/src/hocs/scrollTop.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/hocs/userOnly.tsx b/web/src/hocs/userOnly.tsx index 6992e76b..298c02c7 100644 --- a/web/src/hocs/userOnly.tsx +++ b/web/src/hocs/userOnly.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/libs/config.ts b/web/src/libs/config.ts index 4be40814..6ebf1f63 100644 --- a/web/src/libs/config.ts +++ b/web/src/libs/config.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/libs/countries.js b/web/src/libs/countries.js index 8613b6d3..253705fe 100644 --- a/web/src/libs/countries.js +++ b/web/src/libs/countries.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/libs/dom.ts b/web/src/libs/dom.ts index db7b6217..83ab0d5b 100644 --- a/web/src/libs/dom.ts +++ b/web/src/libs/dom.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/libs/editor.spec.ts b/web/src/libs/editor.spec.ts index 911061ef..894080d1 100644 --- a/web/src/libs/editor.spec.ts +++ b/web/src/libs/editor.spec.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/libs/editor.ts b/web/src/libs/editor.ts index 9a13adb8..eb420c47 100644 --- a/web/src/libs/editor.ts +++ b/web/src/libs/editor.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/libs/encoding.js b/web/src/libs/encoding.js index 4186ab7d..6fcad196 100644 --- a/web/src/libs/encoding.js +++ b/web/src/libs/encoding.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/libs/encoding_test.js b/web/src/libs/encoding_test.js index 3229514c..43354b58 100644 --- a/web/src/libs/encoding_test.js +++ b/web/src/libs/encoding_test.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/libs/fts/lexer.spec.ts b/web/src/libs/fts/lexer.spec.ts index 8ae1bca8..60461a09 100644 --- a/web/src/libs/fts/lexer.spec.ts +++ b/web/src/libs/fts/lexer.spec.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/libs/fts/lexer.ts b/web/src/libs/fts/lexer.ts index 9aaf3a73..1dd0a5b8 100644 --- a/web/src/libs/fts/lexer.ts +++ b/web/src/libs/fts/lexer.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/libs/hooks/dom.ts b/web/src/libs/hooks/dom.ts index 2d03f25a..0f63932e 100644 --- a/web/src/libs/hooks/dom.ts +++ b/web/src/libs/hooks/dom.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/libs/hooks/editor.ts b/web/src/libs/hooks/editor.ts index 72ff981a..460189d0 100644 --- a/web/src/libs/hooks/editor.ts +++ b/web/src/libs/hooks/editor.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/libs/hooks/index.ts b/web/src/libs/hooks/index.ts index 0ab6ba3e..d1424ef3 100644 --- a/web/src/libs/hooks/index.ts +++ b/web/src/libs/hooks/index.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/libs/localStorage.ts b/web/src/libs/localStorage.ts index 41341035..1a2c4e86 100644 --- a/web/src/libs/localStorage.ts +++ b/web/src/libs/localStorage.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/libs/notes.ts b/web/src/libs/notes.ts index dde6c5f8..1dfd7b15 100644 --- a/web/src/libs/notes.ts +++ b/web/src/libs/notes.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/libs/operations.ts b/web/src/libs/operations.ts index 5f71c97e..62ac15ed 100644 --- a/web/src/libs/operations.ts +++ b/web/src/libs/operations.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/libs/paths.spec.ts b/web/src/libs/paths.spec.ts index 463ef363..13f9f0bf 100644 --- a/web/src/libs/paths.spec.ts +++ b/web/src/libs/paths.spec.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/libs/paths.ts b/web/src/libs/paths.ts index e8ad335d..4dea497e 100644 --- a/web/src/libs/paths.ts +++ b/web/src/libs/paths.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/libs/restoreScroll.js b/web/src/libs/restoreScroll.js index 9cc7f252..54cd0943 100644 --- a/web/src/libs/restoreScroll.js +++ b/web/src/libs/restoreScroll.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/libs/scopeTab.js b/web/src/libs/scopeTab.js index d385c7c6..805e7576 100644 --- a/web/src/libs/scopeTab.js +++ b/web/src/libs/scopeTab.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/libs/search.ts b/web/src/libs/search.ts index 1b54218c..404d1987 100644 --- a/web/src/libs/search.ts +++ b/web/src/libs/search.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/libs/services.ts b/web/src/libs/services.ts index 0eb0ef92..ea0b274d 100644 --- a/web/src/libs/services.ts +++ b/web/src/libs/services.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/libs/string.ts b/web/src/libs/string.ts index 821c4261..a6b9dab6 100644 --- a/web/src/libs/string.ts +++ b/web/src/libs/string.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/libs/subscription.js b/web/src/libs/subscription.js index 4cf46ff7..45eff228 100644 --- a/web/src/libs/subscription.js +++ b/web/src/libs/subscription.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/libs/ui.js b/web/src/libs/ui.js index cc76e0dd..42dc1c45 100644 --- a/web/src/libs/ui.js +++ b/web/src/libs/ui.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/routes.tsx b/web/src/routes.tsx index fa3342a8..126989d2 100644 --- a/web/src/routes.tsx +++ b/web/src/routes.tsx @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/store/auth/actions.ts b/web/src/store/auth/actions.ts index 2ecad8cf..3bec2c83 100644 --- a/web/src/store/auth/actions.ts +++ b/web/src/store/auth/actions.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/store/auth/index.ts b/web/src/store/auth/index.ts index 82479ec2..ff39d7a8 100644 --- a/web/src/store/auth/index.ts +++ b/web/src/store/auth/index.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/store/auth/reducers.ts b/web/src/store/auth/reducers.ts index 22f8223d..2eeb20c6 100644 --- a/web/src/store/auth/reducers.ts +++ b/web/src/store/auth/reducers.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/store/auth/type.ts b/web/src/store/auth/type.ts index d1c1982c..d110863d 100644 --- a/web/src/store/auth/type.ts +++ b/web/src/store/auth/type.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/store/books/actions.ts b/web/src/store/books/actions.ts index cd6952b8..e9bcaf6c 100644 --- a/web/src/store/books/actions.ts +++ b/web/src/store/books/actions.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/store/books/index.ts b/web/src/store/books/index.ts index 82479ec2..ff39d7a8 100644 --- a/web/src/store/books/index.ts +++ b/web/src/store/books/index.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/store/books/reducers.ts b/web/src/store/books/reducers.ts index 9749258d..707825a9 100644 --- a/web/src/store/books/reducers.ts +++ b/web/src/store/books/reducers.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/store/books/type.ts b/web/src/store/books/type.ts index 4e3fb5b8..d3c94c9e 100644 --- a/web/src/store/books/type.ts +++ b/web/src/store/books/type.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/store/editor/actions.ts b/web/src/store/editor/actions.ts index 616f8fe1..fe04402d 100644 --- a/web/src/store/editor/actions.ts +++ b/web/src/store/editor/actions.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/store/editor/index.ts b/web/src/store/editor/index.ts index 82479ec2..ff39d7a8 100644 --- a/web/src/store/editor/index.ts +++ b/web/src/store/editor/index.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/store/editor/reducers.ts b/web/src/store/editor/reducers.ts index 4975c2fe..51366d1c 100644 --- a/web/src/store/editor/reducers.ts +++ b/web/src/store/editor/reducers.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/store/editor/type.ts b/web/src/store/editor/type.ts index 614e4f37..7d41c09b 100644 --- a/web/src/store/editor/type.ts +++ b/web/src/store/editor/type.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/store/filters/actions.ts b/web/src/store/filters/actions.ts index a697722f..5d271169 100644 --- a/web/src/store/filters/actions.ts +++ b/web/src/store/filters/actions.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/store/filters/index.ts b/web/src/store/filters/index.ts index 82479ec2..ff39d7a8 100644 --- a/web/src/store/filters/index.ts +++ b/web/src/store/filters/index.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/store/filters/reducers.ts b/web/src/store/filters/reducers.ts index 247c7b61..db2f8fa2 100644 --- a/web/src/store/filters/reducers.ts +++ b/web/src/store/filters/reducers.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/store/filters/type.ts b/web/src/store/filters/type.ts index 5ccf4aa1..a058ea11 100644 --- a/web/src/store/filters/type.ts +++ b/web/src/store/filters/type.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/store/form/actions.ts b/web/src/store/form/actions.ts index ae4a8033..00b86ade 100644 --- a/web/src/store/form/actions.ts +++ b/web/src/store/form/actions.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/store/form/index.ts b/web/src/store/form/index.ts index 82479ec2..ff39d7a8 100644 --- a/web/src/store/form/index.ts +++ b/web/src/store/form/index.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/store/form/reducers.ts b/web/src/store/form/reducers.ts index a69bca14..42a54a58 100644 --- a/web/src/store/form/reducers.ts +++ b/web/src/store/form/reducers.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/store/form/type.ts b/web/src/store/form/type.ts index 8beb7bf1..29f84455 100644 --- a/web/src/store/form/type.ts +++ b/web/src/store/form/type.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/store/hooks.ts b/web/src/store/hooks.ts index 6b92619d..248b7330 100644 --- a/web/src/store/hooks.ts +++ b/web/src/store/hooks.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/store/index.ts b/web/src/store/index.ts index 873d0859..ef44fed1 100644 --- a/web/src/store/index.ts +++ b/web/src/store/index.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/store/note/actions.ts b/web/src/store/note/actions.ts index a3fcaa61..f0b8e078 100644 --- a/web/src/store/note/actions.ts +++ b/web/src/store/note/actions.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/store/note/index.ts b/web/src/store/note/index.ts index 82479ec2..ff39d7a8 100644 --- a/web/src/store/note/index.ts +++ b/web/src/store/note/index.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/store/note/reducers.ts b/web/src/store/note/reducers.ts index a27b74d0..bd2eb640 100644 --- a/web/src/store/note/reducers.ts +++ b/web/src/store/note/reducers.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/store/note/type.ts b/web/src/store/note/type.ts index 7493ecfd..a0233bd8 100644 --- a/web/src/store/note/type.ts +++ b/web/src/store/note/type.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/store/notes/actions.ts b/web/src/store/notes/actions.ts index 5628a231..5c2dc1f0 100644 --- a/web/src/store/notes/actions.ts +++ b/web/src/store/notes/actions.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/store/notes/index.ts b/web/src/store/notes/index.ts index 82479ec2..ff39d7a8 100644 --- a/web/src/store/notes/index.ts +++ b/web/src/store/notes/index.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/store/notes/reducers.ts b/web/src/store/notes/reducers.ts index 65f4998d..44b514ec 100644 --- a/web/src/store/notes/reducers.ts +++ b/web/src/store/notes/reducers.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/store/notes/type.ts b/web/src/store/notes/type.ts index 80f47760..a6f2e515 100644 --- a/web/src/store/notes/type.ts +++ b/web/src/store/notes/type.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/store/route/actions.ts b/web/src/store/route/actions.ts index 6f63167e..ffbc05ea 100644 --- a/web/src/store/route/actions.ts +++ b/web/src/store/route/actions.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/store/route/index.ts b/web/src/store/route/index.ts index 82479ec2..ff39d7a8 100644 --- a/web/src/store/route/index.ts +++ b/web/src/store/route/index.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/store/route/reducers.ts b/web/src/store/route/reducers.ts index 596048a2..d72cade6 100644 --- a/web/src/store/route/reducers.ts +++ b/web/src/store/route/reducers.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/store/route/type.ts b/web/src/store/route/type.ts index d2d65e97..8548d1db 100644 --- a/web/src/store/route/type.ts +++ b/web/src/store/route/type.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/store/types.ts b/web/src/store/types.ts index 4142d4a4..ef88d595 100644 --- a/web/src/store/types.ts +++ b/web/src/store/types.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/store/ui/actions.ts b/web/src/store/ui/actions.ts index 553b3650..882f867a 100644 --- a/web/src/store/ui/actions.ts +++ b/web/src/store/ui/actions.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/store/ui/index.ts b/web/src/store/ui/index.ts index 82479ec2..ff39d7a8 100644 --- a/web/src/store/ui/index.ts +++ b/web/src/store/ui/index.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/store/ui/reducers.ts b/web/src/store/ui/reducers.ts index a83f49b9..0a99180e 100644 --- a/web/src/store/ui/reducers.ts +++ b/web/src/store/ui/reducers.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/src/store/ui/type.ts b/web/src/store/ui/type.ts index fd1dd370..e87ba115 100644 --- a/web/src/store/ui/type.ts +++ b/web/src/store/ui/type.ts @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/webpack/dev.config.js b/web/webpack/dev.config.js index 513e66be..101f4db1 100644 --- a/web/webpack/dev.config.js +++ b/web/webpack/dev.config.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/webpack/externals.js b/web/webpack/externals.js index 7e1f0ec5..2dde3c82 100644 --- a/web/webpack/externals.js +++ b/web/webpack/externals.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/webpack/paths.js b/web/webpack/paths.js index 0fb33285..5a4cce62 100644 --- a/web/webpack/paths.js +++ b/web/webpack/paths.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/webpack/plugins.js b/web/webpack/plugins.js index e828361d..31eb30bf 100644 --- a/web/webpack/plugins.js +++ b/web/webpack/plugins.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/webpack/prod.config.js b/web/webpack/prod.config.js index 2312e65c..9f67c23f 100644 --- a/web/webpack/prod.config.js +++ b/web/webpack/prod.config.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/webpack/resolve.js b/web/webpack/resolve.js index 23ea6afd..baed2e72 100644 --- a/web/webpack/resolve.js +++ b/web/webpack/resolve.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/webpack/rules/css.js b/web/webpack/rules/css.js index 4aac745a..0dddbaac 100644 --- a/web/webpack/rules/css.js +++ b/web/webpack/rules/css.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/webpack/rules/image.js b/web/webpack/rules/image.js index 116b3e1b..b83dd6f6 100644 --- a/web/webpack/rules/image.js +++ b/web/webpack/rules/image.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/webpack/rules/index.js b/web/webpack/rules/index.js index b5888992..29777828 100644 --- a/web/webpack/rules/index.js +++ b/web/webpack/rules/index.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * diff --git a/web/webpack/rules/javascript.js b/web/webpack/rules/javascript.js index 77b93c32..67c1e64b 100644 --- a/web/webpack/rules/javascript.js +++ b/web/webpack/rules/javascript.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd * * This file is part of Dnote. * From 9a054e9ff647dbb63856b5c197fa276c1a59e06e Mon Sep 17 00:00:00 2001 From: Sung Won Cho Date: Sat, 12 Feb 2022 09:11:44 +1100 Subject: [PATCH 08/91] Fix build annotation (#578) --- pkg/cli/dirs/dirs_unix.go | 1 - pkg/cli/dirs/dirs_unix_test.go | 1 - 2 files changed, 2 deletions(-) diff --git a/pkg/cli/dirs/dirs_unix.go b/pkg/cli/dirs/dirs_unix.go index faf4682b..ee1b0f09 100644 --- a/pkg/cli/dirs/dirs_unix.go +++ b/pkg/cli/dirs/dirs_unix.go @@ -15,7 +15,6 @@ * You should have received a copy of the GNU General Public License * along with Dnote. If not, see . */ - // +build linux darwin package dirs diff --git a/pkg/cli/dirs/dirs_unix_test.go b/pkg/cli/dirs/dirs_unix_test.go index 90c9e6d5..dac1470f 100644 --- a/pkg/cli/dirs/dirs_unix_test.go +++ b/pkg/cli/dirs/dirs_unix_test.go @@ -15,7 +15,6 @@ * You should have received a copy of the GNU General Public License * along with Dnote. If not, see . */ - // +build linux darwin package dirs From 172f608b664699b8dc622c3a62365675169abcac Mon Sep 17 00:00:00 2001 From: Sung Won Cho Date: Thu, 17 Feb 2022 20:06:15 +1100 Subject: [PATCH 09/91] Remove Forum (#582) --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index a53da6a1..34dea743 100644 --- a/README.md +++ b/README.md @@ -37,5 +37,4 @@ Please see [Dnote wiki](https://github.com/dnote/dnote/wiki) for the documentati ## See Also - [Homepage](https://www.getdnote.com) -- [Forum](https://forum.getdnote.com) - [I Wrote Down Everything I Learned While Programming for a Month](https://www.getdnote.com/blog/writing-everything-i-learn-coding-for-a-month/) From 01a378c5b162c73052beb76ab34796fb66079df7 Mon Sep 17 00:00:00 2001 From: Sung Won Cho Date: Sun, 24 Apr 2022 10:54:39 +1000 Subject: [PATCH 10/91] Simplify by removing web interface (#590) * Implement MVC * Implement settings * Improve layout * Lock sass dependency --- Makefile | 2 + go.mod | 12 +- go.sum | 49 +- pkg/server/.gitignore | 1 + pkg/server/api/auth.go | 187 --- pkg/server/api/auth_test.go | 408 ----- pkg/server/api/helpers.go | 85 - pkg/server/api/notes.go | 324 ---- pkg/server/api/notes_test.go | 357 ----- pkg/server/api/routes.go | 116 -- pkg/server/api/routes_test.go | 161 -- pkg/server/api/user.go | 394 ----- pkg/server/api/user_test.go | 691 -------- pkg/server/api/v3_auth.go | 226 --- pkg/server/api/v3_auth_test.go | 482 ------ pkg/server/api/v3_books.go | 267 ---- pkg/server/api/v3_notes.go | 220 --- pkg/server/api/v3_notes_test.go | 394 ----- pkg/server/app/app.go | 1 + pkg/server/app/email.go | 8 + pkg/server/app/errors.go | 67 + pkg/server/app/notes.go | 144 ++ pkg/server/app/testutils.go | 7 + pkg/server/app/users.go | 62 +- pkg/server/app/users_test.go | 55 +- pkg/server/assets/js/build.sh | 24 + pkg/server/assets/js/src/main.js | 59 + pkg/server/assets/package-lock.json | 157 ++ pkg/server/assets/package.json | 12 + pkg/server/assets/static/500.html | 10 + .../assets/static/android-icon-144x144.png | Bin 0 -> 2448 bytes .../assets/static/android-icon-192x192.png | Bin 0 -> 1731 bytes .../assets/static/android-icon-36x36.png | Bin 0 -> 1406 bytes .../assets/static/android-icon-48x48.png | Bin 0 -> 1454 bytes .../assets/static/android-icon-72x72.png | Bin 0 -> 1583 bytes .../assets/static/android-icon-96x96.png | Bin 0 -> 1777 bytes .../assets/static/apple-icon-114x114.png | Bin 0 -> 2154 bytes .../assets/static/apple-icon-120x120.png | Bin 0 -> 2181 bytes .../assets/static/apple-icon-144x144.png | Bin 0 -> 2448 bytes .../assets/static/apple-icon-152x152.png | Bin 0 -> 2709 bytes .../assets/static/apple-icon-180x180.png | Bin 0 -> 3177 bytes pkg/server/assets/static/apple-icon-57x57.png | Bin 0 -> 1647 bytes pkg/server/assets/static/apple-icon-60x60.png | Bin 0 -> 1566 bytes pkg/server/assets/static/apple-icon-72x72.png | Bin 0 -> 1583 bytes pkg/server/assets/static/apple-icon-76x76.png | Bin 0 -> 1676 bytes .../assets/static/apple-icon-precomposed.png | Bin 0 -> 2303 bytes pkg/server/assets/static/apple-icon.png | Bin 0 -> 2303 bytes pkg/server/assets/static/browserconfig.xml | 2 + pkg/server/assets/static/favicon-16x16.png | Bin 0 -> 1061 bytes pkg/server/assets/static/favicon-32x32.png | Bin 0 -> 1339 bytes pkg/server/assets/static/favicon-96x96.png | Bin 0 -> 1777 bytes pkg/server/assets/static/favicon.ico | Bin 0 -> 1150 bytes pkg/server/assets/static/logo-512x512.png | Bin 0 -> 4155 bytes pkg/server/assets/static/manifest.json | 52 + pkg/server/assets/static/ms-icon-144x144.png | Bin 0 -> 2448 bytes pkg/server/assets/static/ms-icon-150x150.png | Bin 0 -> 2665 bytes pkg/server/assets/static/ms-icon-310x310.png | Bin 0 -> 8005 bytes pkg/server/assets/static/ms-icon-70x70.png | Bin 0 -> 1685 bytes pkg/server/assets/static/offline.html | 41 + pkg/server/assets/styles/build.sh | 24 + pkg/server/assets/styles/src/_books.scss | 11 + pkg/server/assets/styles/src/_bootstrap.scss | 176 +++ pkg/server/assets/styles/src/_buttons.scss | 182 +++ pkg/server/assets/styles/src/_font.scss | 111 ++ pkg/server/assets/styles/src/_global.scss | 85 + pkg/server/assets/styles/src/_grid.scss | 1108 +++++++++++++ pkg/server/assets/styles/src/_header.scss | 192 +++ pkg/server/assets/styles/src/_hljs.scss | 147 ++ pkg/server/assets/styles/src/_home.scss | 185 +++ pkg/server/assets/styles/src/_login.scss | 88 ++ pkg/server/assets/styles/src/_markdown.scss | 966 ++++++++++++ pkg/server/assets/styles/src/_marker.scss | 39 + pkg/server/assets/styles/src/_note.scss | 102 ++ pkg/server/assets/styles/src/_reboot.scss | 367 +++++ pkg/server/assets/styles/src/_rem.scss | 116 ++ pkg/server/assets/styles/src/_responsive.scss | 62 + pkg/server/assets/styles/src/_select.scss | 463 ++++++ pkg/server/assets/styles/src/_settings.scss | 147 ++ pkg/server/assets/styles/src/_shared.scss | 241 +++ pkg/server/assets/styles/src/_theme.scss | 47 + .../styles/src/_variables.scss} | 17 +- pkg/server/assets/styles/src/main.scss | 144 ++ pkg/server/buildinfo/info.go | 15 + pkg/server/config/config.go | 41 + pkg/server/consts/consts.go | 10 + pkg/server/context/user.go | 64 + pkg/server/controllers/books.go | 297 ++++ .../books_test.go} | 547 ++++--- pkg/server/controllers/controllers.go | 32 + pkg/server/controllers/health.go | 23 + .../{api => controllers}/health_test.go | 17 +- pkg/server/controllers/helpers.go | 315 ++++ .../{job/remind => controllers}/main_test.go | 2 +- pkg/server/controllers/notes.go | 446 ++++++ pkg/server/controllers/notes_test.go | 770 +++++++++ pkg/server/controllers/routes.go | 124 ++ pkg/server/controllers/routes_test.go | 77 + pkg/server/controllers/static.go | 35 + .../{api/v3_sync.go => controllers/sync.go} | 56 +- .../sync_test.go} | 2 +- pkg/server/{api => controllers}/testutils.go | 29 +- pkg/server/controllers/users.go | 718 +++++++++ pkg/server/controllers/users_test.go | 1388 +++++++++++++++++ pkg/server/database/errors.go | 12 + .../{handlers/main_test.go => helpers/url.go} | 23 +- pkg/server/helpers/url_test.go | 29 + pkg/server/helpers/{helpers.go => uuid.go} | 0 pkg/server/job/job.go | 26 - pkg/server/job/remind/inactive.go | 210 --- pkg/server/job/remind/inactive_test.go | 194 --- .../mailer/templates/src/reset_password.txt | 2 +- .../templates/src/reset_password_alert.txt | 2 +- .../src/subscription_confirmation.txt | 4 +- .../mailer/templates/src/verify_email.txt | 6 +- pkg/server/mailer/templates/src/welcome.txt | 2 +- pkg/server/main.go | 107 +- pkg/server/{handlers => middleware}/auth.go | 59 +- .../{handlers => middleware}/helpers.go | 13 +- .../{handlers => middleware}/helpers_test.go | 8 +- pkg/server/{handlers => middleware}/limit.go | 14 +- .../{handlers => middleware}/logging.go | 4 +- pkg/server/{api => middleware}/main_test.go | 2 +- pkg/server/middleware/middleware.go | 76 + pkg/server/net/writer.go | 31 + pkg/server/operations/notes.go | 4 +- pkg/server/operations/notes_test.go | 4 +- pkg/server/static/main.css | 12 + pkg/server/static/main.css.map | 1 + pkg/server/testutils/main.go | 80 + pkg/server/tmpl/data.go | 6 +- pkg/server/views/books/index.gohtml | 20 + pkg/server/views/books/show.gohtml | 4 + pkg/server/views/data.go | 152 ++ pkg/server/views/helpers.go | 176 +++ pkg/server/views/helpers_test.go | 183 +++ pkg/server/views/icons/book.gohtml | 17 + pkg/server/views/icons/caret.gohtml | 26 + pkg/server/views/icons/lock.gohtml | 10 + pkg/server/views/icons/logo.gohtml | 14 + pkg/server/views/icons/logo_with_text.gohtml | 26 + pkg/server/views/layouts/alert.gohtml | 9 + pkg/server/views/layouts/base.gohtml | 40 + pkg/server/views/layouts/css.gohtml | 5 + pkg/server/views/layouts/header.gohtml | 7 + pkg/server/views/layouts/js.gohtml | 5 + pkg/server/views/layouts/navbar.gohtml | 68 + pkg/server/views/notes/index.gohtml | 91 ++ pkg/server/views/notes/show.gohtml | 33 + pkg/server/views/partials/page_toolbar.gohtml | 5 + .../views/partials/settings_sidebar.gohtml | 23 + pkg/server/views/partials/time.gohtml | 13 + pkg/server/views/static/not_found.gohtml | 3 + pkg/server/views/time.go | 103 ++ .../views/users/email_verification.gohtml | 2 + pkg/server/views/users/login.gohtml | 76 + pkg/server/views/users/new.gohtml | 86 + pkg/server/views/users/password_reset.gohtml | 52 + .../views/users/password_reset_confirm.gohtml | 66 + pkg/server/views/users/settings.gohtml | 241 +++ pkg/server/views/users/settings_about.gohtml | 57 + pkg/server/views/view.go | 203 +++ pkg/server/web/handlers.go | 6 +- pkg/watcher/main.go | 21 +- scripts/server/test-local.sh | 4 +- scripts/server/test.sh | 10 +- scripts/vagrant/install_utils.sh | 7 + scripts/web/dev.sh | 40 +- test/cli/test-cli | Bin 0 -> 16934752 bytes 168 files changed, 12773 insertions(+), 5167 deletions(-) delete mode 100644 pkg/server/api/auth.go delete mode 100644 pkg/server/api/auth_test.go delete mode 100644 pkg/server/api/helpers.go delete mode 100644 pkg/server/api/notes.go delete mode 100644 pkg/server/api/notes_test.go delete mode 100644 pkg/server/api/routes.go delete mode 100644 pkg/server/api/routes_test.go delete mode 100644 pkg/server/api/user.go delete mode 100644 pkg/server/api/user_test.go delete mode 100644 pkg/server/api/v3_auth.go delete mode 100644 pkg/server/api/v3_auth_test.go delete mode 100644 pkg/server/api/v3_books.go delete mode 100644 pkg/server/api/v3_notes.go delete mode 100644 pkg/server/api/v3_notes_test.go create mode 100644 pkg/server/app/errors.go create mode 100755 pkg/server/assets/js/build.sh create mode 100644 pkg/server/assets/js/src/main.js create mode 100644 pkg/server/assets/package-lock.json create mode 100644 pkg/server/assets/package.json create mode 100644 pkg/server/assets/static/500.html create mode 100644 pkg/server/assets/static/android-icon-144x144.png create mode 100644 pkg/server/assets/static/android-icon-192x192.png create mode 100644 pkg/server/assets/static/android-icon-36x36.png create mode 100644 pkg/server/assets/static/android-icon-48x48.png create mode 100644 pkg/server/assets/static/android-icon-72x72.png create mode 100644 pkg/server/assets/static/android-icon-96x96.png create mode 100644 pkg/server/assets/static/apple-icon-114x114.png create mode 100644 pkg/server/assets/static/apple-icon-120x120.png create mode 100644 pkg/server/assets/static/apple-icon-144x144.png create mode 100644 pkg/server/assets/static/apple-icon-152x152.png create mode 100644 pkg/server/assets/static/apple-icon-180x180.png create mode 100644 pkg/server/assets/static/apple-icon-57x57.png create mode 100644 pkg/server/assets/static/apple-icon-60x60.png create mode 100644 pkg/server/assets/static/apple-icon-72x72.png create mode 100644 pkg/server/assets/static/apple-icon-76x76.png create mode 100644 pkg/server/assets/static/apple-icon-precomposed.png create mode 100644 pkg/server/assets/static/apple-icon.png create mode 100644 pkg/server/assets/static/browserconfig.xml create mode 100644 pkg/server/assets/static/favicon-16x16.png create mode 100644 pkg/server/assets/static/favicon-32x32.png create mode 100644 pkg/server/assets/static/favicon-96x96.png create mode 100644 pkg/server/assets/static/favicon.ico create mode 100644 pkg/server/assets/static/logo-512x512.png create mode 100644 pkg/server/assets/static/manifest.json create mode 100644 pkg/server/assets/static/ms-icon-144x144.png create mode 100644 pkg/server/assets/static/ms-icon-150x150.png create mode 100644 pkg/server/assets/static/ms-icon-310x310.png create mode 100644 pkg/server/assets/static/ms-icon-70x70.png create mode 100644 pkg/server/assets/static/offline.html create mode 100755 pkg/server/assets/styles/build.sh create mode 100644 pkg/server/assets/styles/src/_books.scss create mode 100644 pkg/server/assets/styles/src/_bootstrap.scss create mode 100644 pkg/server/assets/styles/src/_buttons.scss create mode 100644 pkg/server/assets/styles/src/_font.scss create mode 100644 pkg/server/assets/styles/src/_global.scss create mode 100644 pkg/server/assets/styles/src/_grid.scss create mode 100644 pkg/server/assets/styles/src/_header.scss create mode 100644 pkg/server/assets/styles/src/_hljs.scss create mode 100644 pkg/server/assets/styles/src/_home.scss create mode 100644 pkg/server/assets/styles/src/_login.scss create mode 100644 pkg/server/assets/styles/src/_markdown.scss create mode 100644 pkg/server/assets/styles/src/_marker.scss create mode 100644 pkg/server/assets/styles/src/_note.scss create mode 100644 pkg/server/assets/styles/src/_reboot.scss create mode 100644 pkg/server/assets/styles/src/_rem.scss create mode 100644 pkg/server/assets/styles/src/_responsive.scss create mode 100644 pkg/server/assets/styles/src/_select.scss create mode 100644 pkg/server/assets/styles/src/_settings.scss create mode 100644 pkg/server/assets/styles/src/_shared.scss create mode 100644 pkg/server/assets/styles/src/_theme.scss rename pkg/server/{api/health.go => assets/styles/src/_variables.scss} (76%) create mode 100644 pkg/server/assets/styles/src/main.scss create mode 100644 pkg/server/buildinfo/info.go create mode 100644 pkg/server/consts/consts.go create mode 100644 pkg/server/context/user.go create mode 100644 pkg/server/controllers/books.go rename pkg/server/{api/v3_books_test.go => controllers/books_test.go} (65%) create mode 100644 pkg/server/controllers/controllers.go create mode 100644 pkg/server/controllers/health.go rename pkg/server/{api => controllers}/health_test.go (80%) create mode 100644 pkg/server/controllers/helpers.go rename pkg/server/{job/remind => controllers}/main_test.go (97%) create mode 100644 pkg/server/controllers/notes.go create mode 100644 pkg/server/controllers/notes_test.go create mode 100644 pkg/server/controllers/routes.go create mode 100644 pkg/server/controllers/routes_test.go create mode 100644 pkg/server/controllers/static.go rename pkg/server/{api/v3_sync.go => controllers/sync.go} (83%) rename pkg/server/{api/v3_sync_test.go => controllers/sync_test.go} (99%) rename pkg/server/{api => controllers}/testutils.go (67%) create mode 100644 pkg/server/controllers/users.go create mode 100644 pkg/server/controllers/users_test.go create mode 100644 pkg/server/database/errors.go rename pkg/server/{handlers/main_test.go => helpers/url.go} (70%) create mode 100644 pkg/server/helpers/url_test.go rename pkg/server/helpers/{helpers.go => uuid.go} (100%) delete mode 100644 pkg/server/job/remind/inactive.go delete mode 100644 pkg/server/job/remind/inactive_test.go rename pkg/server/{handlers => middleware}/auth.go (76%) rename pkg/server/{handlers => middleware}/helpers.go (91%) rename pkg/server/{handlers => middleware}/helpers_test.go (98%) rename pkg/server/{handlers => middleware}/limit.go (92%) rename pkg/server/{handlers => middleware}/logging.go (97%) rename pkg/server/{api => middleware}/main_test.go (98%) create mode 100644 pkg/server/middleware/middleware.go create mode 100644 pkg/server/net/writer.go create mode 100644 pkg/server/static/main.css create mode 100644 pkg/server/static/main.css.map create mode 100644 pkg/server/views/books/index.gohtml create mode 100644 pkg/server/views/books/show.gohtml create mode 100644 pkg/server/views/data.go create mode 100644 pkg/server/views/helpers.go create mode 100644 pkg/server/views/helpers_test.go create mode 100644 pkg/server/views/icons/book.gohtml create mode 100644 pkg/server/views/icons/caret.gohtml create mode 100644 pkg/server/views/icons/lock.gohtml create mode 100644 pkg/server/views/icons/logo.gohtml create mode 100644 pkg/server/views/icons/logo_with_text.gohtml create mode 100644 pkg/server/views/layouts/alert.gohtml create mode 100644 pkg/server/views/layouts/base.gohtml create mode 100644 pkg/server/views/layouts/css.gohtml create mode 100644 pkg/server/views/layouts/header.gohtml create mode 100644 pkg/server/views/layouts/js.gohtml create mode 100644 pkg/server/views/layouts/navbar.gohtml create mode 100644 pkg/server/views/notes/index.gohtml create mode 100644 pkg/server/views/notes/show.gohtml create mode 100644 pkg/server/views/partials/page_toolbar.gohtml create mode 100644 pkg/server/views/partials/settings_sidebar.gohtml create mode 100644 pkg/server/views/partials/time.gohtml create mode 100644 pkg/server/views/static/not_found.gohtml create mode 100644 pkg/server/views/time.go create mode 100644 pkg/server/views/users/email_verification.gohtml create mode 100644 pkg/server/views/users/login.gohtml create mode 100644 pkg/server/views/users/new.gohtml create mode 100644 pkg/server/views/users/password_reset.gohtml create mode 100644 pkg/server/views/users/password_reset_confirm.gohtml create mode 100644 pkg/server/views/users/settings.gohtml create mode 100644 pkg/server/views/users/settings_about.gohtml create mode 100644 pkg/server/views/view.go create mode 100755 test/cli/test-cli diff --git a/Makefile b/Makefile index c7a3eccd..c0dedcdc 100644 --- a/Makefile +++ b/Makefile @@ -30,11 +30,13 @@ endif ifeq ($(CI), true) @(cd ${currentDir} && npm ci --cache $(NPM_CACHE_DIR) --prefer-offline --unsafe-perm=true) + @(cd ${currentDir}/pkg/server/assets && npm ci --cache $(NPM_CACHE_DIR) --prefer-offline --unsafe-perm=true) @(cd ${currentDir}/web && npm ci --cache $(NPM_CACHE_DIR) --prefer-offline --unsafe-perm=true) @(cd ${currentDir}/browser && npm ci --cache $(NPM_CACHE_DIR) --prefer-offline --unsafe-perm=true) @(cd ${currentDir}/jslib && npm ci --cache $(NPM_CACHE_DIR) --prefer-offline --unsafe-perm=true) else @(cd ${currentDir} && npm install) + @(cd ${currentDir}/pkg/server/assets && npm install) @(cd ${currentDir}/web && npm install) @(cd ${currentDir}/browser && npm install) @(cd ${currentDir}/jslib && npm install) diff --git a/go.mod b/go.mod index 14d37869..0150d739 100644 --- a/go.mod +++ b/go.mod @@ -8,34 +8,36 @@ require ( github.com/aymerick/douceur v0.2.0 github.com/dnote/actions v0.2.0 github.com/dnote/color v1.7.0 - github.com/dnote/xgo v0.0.0-20200205013105-40be7d6d43ff // indirect github.com/gobuffalo/packr/v2 v2.8.1 github.com/google/go-cmp v0.5.4 github.com/google/go-github v17.0.0+incompatible - github.com/google/go-querystring v1.0.0 // indirect github.com/google/uuid v1.1.3 - github.com/gorilla/css v1.0.0 // indirect + github.com/gorilla/csrf v1.6.2 github.com/gorilla/mux v1.8.0 + github.com/gorilla/schema v1.2.0 github.com/jinzhu/gorm v1.9.16 github.com/joho/godotenv v1.3.0 github.com/karrick/godirwalk v1.16.1 // indirect github.com/lib/pq v1.9.0 github.com/mattn/go-colorable v0.1.8 // indirect github.com/mattn/go-sqlite3 v1.14.6 + github.com/nadproject/nad v0.0.0-20200124233812-f1a4e763ee2f github.com/pkg/errors v0.9.1 github.com/radovskyb/watcher v1.0.7 github.com/robfig/cron v1.2.0 + github.com/rogpeppe/go-internal v1.6.2 // indirect github.com/rubenv/sql-migrate v0.0.0-20200616145509-8d140a17f351 github.com/sergi/go-diff v1.1.0 github.com/sirupsen/logrus v1.7.0 // indirect github.com/spf13/cobra v1.1.1 + github.com/yuin/goldmark v1.4.0 golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad golang.org/x/net v0.0.0-20201224014010-6772e930b67b // indirect - golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a // indirect + golang.org/x/sync v0.0.0-20201207232520-09787c993a3a // indirect golang.org/x/sys v0.0.0-20201231184435-2d18734c6014 // indirect golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 - gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index 238bdc3a..41625712 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,6 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= @@ -15,6 +16,7 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg= github.com/PuerkitoBio/goquery v1.5.1 h1:PSPBGne8NIUWw+/7vFBV+kG2J/5MOjbzc7154OaKCSE= github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= github.com/PuerkitoBio/goquery v1.6.0 h1:j7taAbelrdcsOlGeMenZxc2AWXD5fieT1/znArdnx94= @@ -27,6 +29,7 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= github.com/andybalholm/cascadia v1.2.0 h1:vuRCkM5Ozh/BfmsaTm26kbjm0mIOM3yS5Ek/F5h18aE= github.com/andybalholm/cascadia v1.2.0/go.mod h1:YCyR8vOZT9aZ1CHEd8ap0gMVm2aFgxBp0T0eFw1RUQY= @@ -73,6 +76,7 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM= github.com/denisenkom/go-mssqldb v0.0.0-20191001013358-cfbb681360f0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM= github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= @@ -82,8 +86,6 @@ github.com/dnote/actions v0.2.0 h1:P1ut2/QRKwfAzIIB374vN9A4IanU94C/payEocvngYo= github.com/dnote/actions v0.2.0/go.mod h1:bBIassLhppVQdbC3iaE92SHBpM1HOVe+xZoAlj9ROxw= github.com/dnote/color v1.7.0 h1:8/QGLQKSU8/zcWQaHbMyC1hJRkKO/Uu9M89sH76ecHE= github.com/dnote/color v1.7.0/go.mod h1:75UcP/TH7CNvjQ5pwDumkUS3vkPdGggy7/3fT8MlxHM= -github.com/dnote/xgo v0.0.0-20200205013105-40be7d6d43ff h1:DJKdzouhr6u1NzuLbmSWeei9BagH3Nm4mSOzP0RMdc0= -github.com/dnote/xgo v0.0.0-20200205013105-40be7d6d43ff/go.mod h1:ruGZjl8WThApI7BAIKV2Q/PnJoudvd6Epjc3z79jWVg= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= @@ -113,12 +115,16 @@ github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= github.com/gobuffalo/envy v1.7.1/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w= +github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs= github.com/gobuffalo/logger v1.0.1/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs= github.com/gobuffalo/logger v1.0.3 h1:YaXOTHNPCvkqqA7w05A4v0k2tCdpr+sgFlgINbQ6gqc= github.com/gobuffalo/logger v1.0.3/go.mod h1:SoeejUwldiS7ZsyCBphOGURmWdwUFXs0J7TCjEhjKxM= github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q= github.com/gobuffalo/packd v1.0.0 h1:6ERZvJHfe24rfFmA9OaoKBdC7+c9sydrytMg8SdFGBM= github.com/gobuffalo/packd v1.0.0/go.mod h1:6VTc4htmJRFB7u1m/4LeMTWjFoYrUiBkU9Fdec9hrhI= +github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg= +github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk= +github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw= github.com/gobuffalo/packr/v2 v2.7.1/go.mod h1:qYEvAazPaVxy7Y7KR0W8qYEE+RymX74kETFqjFoFlOc= github.com/gobuffalo/packr/v2 v2.8.1 h1:tkQpju6i3EtMXJ9uoF5GT6kB+LMTimDWD8Xvbz6zDVA= github.com/gobuffalo/packr/v2 v2.8.1/go.mod h1:c/PLlOuTU+p3SybaJATW3H6lX/iK7xEz5OeMf+NnJpg= @@ -158,18 +164,27 @@ github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OI github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.3 h1:twObb+9XcuH5B9V1TBCvvvZoO6iEdILi2a76PYn5rJI= github.com/google/uuid v1.1.3/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/csrf v1.6.2 h1:QqQ/OWwuFp4jMKgBFAzJVW3FMULdyUW7JoM4pEWuqKg= +github.com/gorilla/csrf v1.6.2/go.mod h1:7tSf8kmjNYr7IWDCYhd3U8Ck34iQ/Yw5CJu7bAkHEGI= github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/schema v1.1.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU= +github.com/gorilla/schema v1.2.0 h1:YufUaxZYCKGFuAq3c96BOhjgd5nmXiOY9NGzF247Tsc= +github.com/gorilla/schema v1.2.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU= +github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= +github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= @@ -206,6 +221,7 @@ github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmK github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/jinzhu/gorm v1.9.9/go.mod h1:Kh6hTsSGffh4ui079FHrR5Gg+5D0hgihqDcsDN2BBJY= github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o= github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= @@ -222,6 +238,9 @@ github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/justincampbell/bigduration v0.0.0-20160531141349-e45bf03c0666/go.mod h1:xqGOmDZzLOG7+q/CgsbXv10g4tgPsbjhmAxyaTJMvis= +github.com/justincampbell/timeago v0.0.0-20160528003754-027f40306f1d/go.mod h1:U7FWcK1jzZJnYuSnxP6efX3ZoHbK1CEpD0ThYyGNPNI= +github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= github.com/karrick/godirwalk v1.15.8/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw= github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= @@ -255,17 +274,17 @@ github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-oci8 v0.0.7/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mNXJwGI= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.12.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus= github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U= -github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= @@ -281,6 +300,9 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/nadproject/color v1.7.0/go.mod h1:p2KusS2iX8Q7ncpngDmtva/kZmiad9Hv5MFS4SLuCZQ= +github.com/nadproject/nad v0.0.0-20200124233812-f1a4e763ee2f h1:Vq2SFUt+Mrle7Irf7rLOnYBegSVF3tyNbsMnDomWfH8= +github.com/nadproject/nad v0.0.0-20200124233812-f1a4e763ee2f/go.mod h1:mGl2lRU9Xo49kzVYj46FwP+pEP/Um+nIqTdCmPHtI5k= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= @@ -354,13 +376,18 @@ github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE github.com/rogpeppe/go-internal v1.4.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.5.2 h1:qLvObTrvO/XRCqmkKxUlOBc48bI3efyDuAZe25QiF0w= github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.6.2 h1:aIihoIOHCiLZHxyoNQ+ABL4NKhFTgKLBdMLyEAh98m0= +github.com/rogpeppe/go-internal v1.6.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rubenv/sql-migrate v0.0.0-20190618074426-f4d34eae5a5c/go.mod h1:WS0rl9eEliYI8DPnr3TOwz4439pay+qNgzJoVya/DmY= github.com/rubenv/sql-migrate v0.0.0-20200616145509-8d140a17f351 h1:HXr/qUllAWv9riaI4zh2eXWKmCSDqVS/XH1MRHLKRwk= github.com/rubenv/sql-migrate v0.0.0-20200616145509-8d140a17f351/go.mod h1:DCgfY80j8GYL7MLEfvcpSFvjD0L5yZq/aZUJmhZklyg= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= @@ -398,6 +425,7 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stripe/stripe-go v61.7.1+incompatible/go.mod h1:A1dQZmO/QypXmsL0T8axYZkSN/uA/T/A64pfKdBAMiY= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= @@ -407,6 +435,8 @@ github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijb github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.4.0 h1:OtISOGfH6sOWa1/qXqqAiOIAO6Z5J3AEAE18WAq6BiQ= +github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs= github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= @@ -493,8 +523,8 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a h1:DcqTD9SDLc+1P/r1EmRBwnVsrOwW+kk2vWf9n+1sGhs= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -505,6 +535,7 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -552,6 +583,7 @@ golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -562,11 +594,14 @@ golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200308013534-11ec41452d41 h1:9Di9iYgOt9ThCipBxChBVhgNipDoE5mxO84rQV7D0FE= golang.org/x/tools v0.0.0-20200308013534-11ec41452d41/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= @@ -581,6 +616,7 @@ google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -610,6 +646,7 @@ gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qS gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= +gopkg.in/gomail.v2 v2.0.0-20150902115704-41f357289737/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE= gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= gopkg.in/gorp.v1 v1.7.2 h1:j3DWlAyGVv8whO7AcIWznQ2Yj7yJkn34B8s63GViAAw= diff --git a/pkg/server/.gitignore b/pkg/server/.gitignore index fa49c96a..9835f3e3 100644 --- a/pkg/server/.gitignore +++ b/pkg/server/.gitignore @@ -6,3 +6,4 @@ test-dnote /dist /build server +/static diff --git a/pkg/server/api/auth.go b/pkg/server/api/auth.go deleted file mode 100644 index 56425c87..00000000 --- a/pkg/server/api/auth.go +++ /dev/null @@ -1,187 +0,0 @@ -/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd - * - * This file is part of Dnote. - * - * Dnote is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Dnote is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Dnote. If not, see . - */ - -package api - -import ( - "encoding/json" - "net/http" - "time" - - "github.com/dnote/dnote/pkg/server/database" - "github.com/dnote/dnote/pkg/server/handlers" - "github.com/dnote/dnote/pkg/server/helpers" - "github.com/dnote/dnote/pkg/server/log" - "github.com/dnote/dnote/pkg/server/mailer" - "github.com/dnote/dnote/pkg/server/session" - "github.com/dnote/dnote/pkg/server/token" - "github.com/pkg/errors" - "golang.org/x/crypto/bcrypt" -) - -// GetMeResponse is the response for getMe endpoint -type GetMeResponse struct { - User session.Session `json:"user"` -} - -func (a *API) getMe(w http.ResponseWriter, r *http.Request) { - user, ok := r.Context().Value(helpers.KeyUser).(database.User) - if !ok { - handlers.DoError(w, "No authenticated user found", nil, http.StatusInternalServerError) - return - } - - var account database.Account - if err := a.App.DB.Where("user_id = ?", user.ID).First(&account).Error; err != nil { - handlers.DoError(w, "finding account", err, http.StatusInternalServerError) - return - } - - tx := a.App.DB.Begin() - if err := a.App.TouchLastLoginAt(user, tx); err != nil { - tx.Rollback() - // In case of an error, gracefully continue to avoid disturbing the service - log.ErrorWrap(err, "error touching last_login_at") - } - tx.Commit() - - response := GetMeResponse{ - User: session.New(user, account), - } - handlers.RespondJSON(w, http.StatusOK, response) -} - -type createResetTokenPayload struct { - Email string `json:"email"` -} - -func (a *API) createResetToken(w http.ResponseWriter, r *http.Request) { - var params createResetTokenPayload - if err := json.NewDecoder(r.Body).Decode(¶ms); err != nil { - http.Error(w, "invalid payload", http.StatusBadRequest) - return - } - - var account database.Account - conn := a.App.DB.Where("email = ?", params.Email).First(&account) - if conn.RecordNotFound() { - return - } - if err := conn.Error; err != nil { - handlers.DoError(w, errors.Wrap(err, "finding account").Error(), nil, http.StatusInternalServerError) - return - } - - resetToken, err := token.Create(a.App.DB, account.UserID, database.TokenTypeResetPassword) - if err != nil { - handlers.DoError(w, errors.Wrap(err, "generating token").Error(), nil, http.StatusInternalServerError) - return - } - - if err := a.App.SendPasswordResetEmail(account.Email.String, resetToken.Value); err != nil { - if errors.Cause(err) == mailer.ErrSMTPNotConfigured { - handlers.RespondInvalidSMTPConfig(w) - } else { - handlers.DoError(w, errors.Wrap(err, "sending password reset email").Error(), nil, http.StatusInternalServerError) - } - - return - } -} - -type resetPasswordPayload struct { - Password string `json:"password"` - Token string `json:"token"` -} - -func (a *API) resetPassword(w http.ResponseWriter, r *http.Request) { - var params resetPasswordPayload - if err := json.NewDecoder(r.Body).Decode(¶ms); err != nil { - http.Error(w, "invalid payload", http.StatusBadRequest) - return - } - - var token database.Token - conn := a.App.DB.Where("value = ? AND type =? AND used_at IS NULL", params.Token, database.TokenTypeResetPassword).First(&token) - if conn.RecordNotFound() { - http.Error(w, "invalid token", http.StatusBadRequest) - return - } - if err := conn.Error; err != nil { - handlers.DoError(w, errors.Wrap(err, "finding token").Error(), nil, http.StatusInternalServerError) - return - } - - if token.UsedAt != nil { - http.Error(w, "invalid token", http.StatusBadRequest) - return - } - - // Expire after 10 minutes - if time.Since(token.CreatedAt).Minutes() > 10 { - http.Error(w, "This link has been expired. Please request a new password reset link.", http.StatusGone) - return - } - - tx := a.App.DB.Begin() - - hashedPassword, err := bcrypt.GenerateFromPassword([]byte(params.Password), bcrypt.DefaultCost) - if err != nil { - tx.Rollback() - handlers.DoError(w, errors.Wrap(err, "hashing password").Error(), nil, http.StatusInternalServerError) - return - } - - var account database.Account - if err := a.App.DB.Where("user_id = ?", token.UserID).First(&account).Error; err != nil { - tx.Rollback() - handlers.DoError(w, errors.Wrap(err, "finding user").Error(), nil, http.StatusInternalServerError) - return - } - - if err := tx.Model(&account).Update("password", string(hashedPassword)).Error; err != nil { - tx.Rollback() - handlers.DoError(w, errors.Wrap(err, "updating password").Error(), nil, http.StatusInternalServerError) - return - } - if err := tx.Model(&token).Update("used_at", time.Now()).Error; err != nil { - tx.Rollback() - handlers.DoError(w, errors.Wrap(err, "updating password reset token").Error(), nil, http.StatusInternalServerError) - return - } - - if err := a.App.DeleteUserSessions(tx, account.UserID); err != nil { - tx.Rollback() - handlers.DoError(w, errors.Wrap(err, "deleting user sessions").Error(), nil, http.StatusInternalServerError) - return - } - - tx.Commit() - - var user database.User - if err := a.App.DB.Where("id = ?", account.UserID).First(&user).Error; err != nil { - handlers.DoError(w, errors.Wrap(err, "finding user").Error(), nil, http.StatusInternalServerError) - return - } - - a.respondWithSession(a.App.DB, w, user.ID, http.StatusOK) - - if err := a.App.SendPasswordResetAlertEmail(account.Email.String); err != nil { - log.ErrorWrap(err, "sending password reset email") - } -} diff --git a/pkg/server/api/auth_test.go b/pkg/server/api/auth_test.go deleted file mode 100644 index 1e75dd92..00000000 --- a/pkg/server/api/auth_test.go +++ /dev/null @@ -1,408 +0,0 @@ -/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd - * - * This file is part of Dnote. - * - * Dnote is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Dnote is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Dnote. If not, see . - */ - -package api - -import ( - "encoding/json" - "fmt" - "net/http" - "testing" - "time" - - "github.com/dnote/dnote/pkg/assert" - "github.com/dnote/dnote/pkg/clock" - "github.com/dnote/dnote/pkg/server/app" - "github.com/dnote/dnote/pkg/server/database" - "github.com/dnote/dnote/pkg/server/session" - "github.com/dnote/dnote/pkg/server/testutils" - "github.com/pkg/errors" - "golang.org/x/crypto/bcrypt" -) - -func TestGetMe(t *testing.T) { - testutils.InitTestDB() - defer testutils.ClearData(testutils.DB) - - // Setup - server := MustNewServer(t, &app.App{ - Clock: clock.NewMock(), - }) - defer server.Close() - - u1 := testutils.SetupUserData() - a1 := testutils.SetupAccountData(u1, "alice@example.com", "somepassword") - - u2 := testutils.SetupUserData() - testutils.MustExec(t, testutils.DB.Model(&u2).Update("cloud", false), "preparing u2 cloud") - a2 := testutils.SetupAccountData(u2, "bob@example.com", "somepassword") - - testCases := []struct { - user database.User - account database.Account - expectedPro bool - }{ - { - user: u1, - account: a1, - expectedPro: true, - }, - { - user: u2, - account: a2, - expectedPro: false, - }, - } - - for _, tc := range testCases { - t.Run(fmt.Sprintf("user pro %t", tc.expectedPro), func(t *testing.T) { - // Execute - req := testutils.MakeReq(server.URL, "GET", "/me", "") - res := testutils.HTTPAuthDo(t, req, tc.user) - - // Test - assert.StatusCodeEquals(t, res, http.StatusOK, "") - - var payload GetMeResponse - if err := json.NewDecoder(res.Body).Decode(&payload); err != nil { - t.Fatal(errors.Wrap(err, "decoding payload")) - } - - expectedPayload := GetMeResponse{ - User: session.Session{ - UUID: tc.user.UUID, - Pro: tc.expectedPro, - Email: tc.account.Email.String, - EmailVerified: tc.account.EmailVerified, - }, - } - assert.DeepEqual(t, payload, expectedPayload, "payload mismatch") - - var user database.User - testutils.MustExec(t, testutils.DB.Where("id = ?", tc.user.ID).First(&user), "finding user") - assert.NotEqual(t, user.LastLoginAt, nil, "LastLoginAt mismatch") - }) - } -} - -func TestCreateResetToken(t *testing.T) { - t.Run("success", func(t *testing.T) { - defer testutils.ClearData(testutils.DB) - - // Setup - server := MustNewServer(t, &app.App{ - - Clock: clock.NewMock(), - }) - defer server.Close() - - u := testutils.SetupUserData() - testutils.SetupAccountData(u, "alice@example.com", "somepassword") - - dat := `{"email": "alice@example.com"}` - req := testutils.MakeReq(server.URL, "POST", "/reset-token", dat) - - // Execute - res := testutils.HTTPDo(t, req) - - // Test - assert.StatusCodeEquals(t, res, http.StatusOK, "Status code mismtach") - - var tokenCount int - testutils.MustExec(t, testutils.DB.Model(&database.Token{}).Count(&tokenCount), "counting tokens") - - var resetToken database.Token - testutils.MustExec(t, testutils.DB.Where("user_id = ? AND type = ?", u.ID, database.TokenTypeResetPassword).First(&resetToken), "finding reset token") - - assert.Equal(t, tokenCount, 1, "reset_token count mismatch") - assert.NotEqual(t, resetToken.Value, nil, "reset_token value mismatch") - assert.Equal(t, resetToken.UsedAt, (*time.Time)(nil), "reset_token UsedAt mismatch") - }) - - t.Run("nonexistent email", func(t *testing.T) { - - defer testutils.ClearData(testutils.DB) - - // Setup - server := MustNewServer(t, &app.App{ - - Clock: clock.NewMock(), - }) - defer server.Close() - - u := testutils.SetupUserData() - testutils.SetupAccountData(u, "alice@example.com", "somepassword") - - dat := `{"email": "bob@example.com"}` - req := testutils.MakeReq(server.URL, "POST", "/reset-token", dat) - - // Execute - res := testutils.HTTPDo(t, req) - - // Test - assert.StatusCodeEquals(t, res, http.StatusOK, "Status code mismtach") - - var tokenCount int - testutils.MustExec(t, testutils.DB.Model(&database.Token{}).Count(&tokenCount), "counting tokens") - assert.Equal(t, tokenCount, 0, "reset_token count mismatch") - }) -} - -func TestResetPassword(t *testing.T) { - t.Run("success", func(t *testing.T) { - defer testutils.ClearData(testutils.DB) - - // Setup - server := MustNewServer(t, &app.App{ - - Clock: clock.NewMock(), - }) - defer server.Close() - - u := testutils.SetupUserData() - a := testutils.SetupAccountData(u, "alice@example.com", "oldpassword") - tok := database.Token{ - UserID: u.ID, - Value: "MivFxYiSMMA4An9dP24DNQ==", - Type: database.TokenTypeResetPassword, - } - testutils.MustExec(t, testutils.DB.Save(&tok), "preparing token") - otherTok := database.Token{ - UserID: u.ID, - Value: "somerandomvalue", - Type: database.TokenTypeEmailVerification, - } - testutils.MustExec(t, testutils.DB.Save(&otherTok), "preparing another token") - - dat := `{"token": "MivFxYiSMMA4An9dP24DNQ==", "password": "newpassword"}` - req := testutils.MakeReq(server.URL, "PATCH", "/reset-password", dat) - - s1 := database.Session{ - Key: "some-session-key-1", - UserID: u.ID, - ExpiresAt: time.Now().Add(time.Hour * 10 * 24), - } - testutils.MustExec(t, testutils.DB.Save(&s1), "preparing user session 1") - - s2 := &database.Session{ - Key: "some-session-key-2", - UserID: u.ID, - ExpiresAt: time.Now().Add(time.Hour * 10 * 24), - } - testutils.MustExec(t, testutils.DB.Save(&s2), "preparing user session 2") - - anotherUser := testutils.SetupUserData() - testutils.MustExec(t, testutils.DB.Save(&database.Session{ - Key: "some-session-key-3", - UserID: anotherUser.ID, - ExpiresAt: time.Now().Add(time.Hour * 10 * 24), - }), "preparing anotherUser session 1") - - // Execute - res := testutils.HTTPDo(t, req) - - // Test - assert.StatusCodeEquals(t, res, http.StatusOK, "Status code mismatch") - - var resetToken, verificationToken database.Token - var account database.Account - testutils.MustExec(t, testutils.DB.Where("value = ?", "MivFxYiSMMA4An9dP24DNQ==").First(&resetToken), "finding reset token") - testutils.MustExec(t, testutils.DB.Where("value = ?", "somerandomvalue").First(&verificationToken), "finding reset token") - testutils.MustExec(t, testutils.DB.Where("id = ?", a.ID).First(&account), "finding account") - - assert.NotEqual(t, resetToken.UsedAt, nil, "reset_token UsedAt mismatch") - passwordErr := bcrypt.CompareHashAndPassword([]byte(account.Password.String), []byte("newpassword")) - assert.Equal(t, passwordErr, nil, "Password mismatch") - assert.Equal(t, verificationToken.UsedAt, (*time.Time)(nil), "verificationToken UsedAt mismatch") - - var s1Count, s2Count int - testutils.MustExec(t, testutils.DB.Model(&database.Session{}).Where("id = ?", s1.ID).Count(&s1Count), "counting s1") - testutils.MustExec(t, testutils.DB.Model(&database.Session{}).Where("id = ?", s2.ID).Count(&s2Count), "counting s2") - - assert.Equal(t, s1Count, 0, "s1 should have been deleted") - assert.Equal(t, s2Count, 0, "s2 should have been deleted") - - var userSessionCount, anotherUserSessionCount int - testutils.MustExec(t, testutils.DB.Model(&database.Session{}).Where("user_id = ?", u.ID).Count(&userSessionCount), "counting user session") - testutils.MustExec(t, testutils.DB.Model(&database.Session{}).Where("user_id = ?", anotherUser.ID).Count(&anotherUserSessionCount), "counting anotherUser session") - - assert.Equal(t, userSessionCount, 1, "should have created a new user session") - assert.Equal(t, anotherUserSessionCount, 1, "anotherUser session count mismatch") - }) - - t.Run("nonexistent token", func(t *testing.T) { - defer testutils.ClearData(testutils.DB) - - // Setup - server := MustNewServer(t, &app.App{ - - Clock: clock.NewMock(), - }) - defer server.Close() - - u := testutils.SetupUserData() - a := testutils.SetupAccountData(u, "alice@example.com", "somepassword") - tok := database.Token{ - UserID: u.ID, - Value: "MivFxYiSMMA4An9dP24DNQ==", - Type: database.TokenTypeResetPassword, - } - testutils.MustExec(t, testutils.DB.Save(&tok), "preparing token") - - dat := `{"token": "-ApMnyvpg59uOU5b-Kf5uQ==", "password": "oldpassword"}` - req := testutils.MakeReq(server.URL, "PATCH", "/reset-password", dat) - - // Execute - res := testutils.HTTPDo(t, req) - - // Test - assert.StatusCodeEquals(t, res, http.StatusBadRequest, "Status code mismatch") - - var resetToken database.Token - var account database.Account - testutils.MustExec(t, testutils.DB.Where("value = ?", "MivFxYiSMMA4An9dP24DNQ==").First(&resetToken), "finding reset token") - testutils.MustExec(t, testutils.DB.Where("id = ?", a.ID).First(&account), "finding account") - - assert.Equal(t, a.Password, account.Password, "password should not have been updated") - assert.Equal(t, a.Password, account.Password, "password should not have been updated") - assert.Equal(t, resetToken.UsedAt, (*time.Time)(nil), "used_at should be nil") - }) - - t.Run("expired token", func(t *testing.T) { - defer testutils.ClearData(testutils.DB) - - // Setup - server := MustNewServer(t, &app.App{ - - Clock: clock.NewMock(), - }) - defer server.Close() - - u := testutils.SetupUserData() - a := testutils.SetupAccountData(u, "alice@example.com", "somepassword") - tok := database.Token{ - UserID: u.ID, - Value: "MivFxYiSMMA4An9dP24DNQ==", - Type: database.TokenTypeResetPassword, - } - testutils.MustExec(t, testutils.DB.Save(&tok), "preparing token") - testutils.MustExec(t, testutils.DB.Model(&tok).Update("created_at", time.Now().Add(time.Minute*-11)), "Failed to prepare reset_token created_at") - - dat := `{"token": "MivFxYiSMMA4An9dP24DNQ==", "password": "oldpassword"}` - req := testutils.MakeReq(server.URL, "PATCH", "/reset-password", dat) - - // Execute - res := testutils.HTTPDo(t, req) - - // Test - assert.StatusCodeEquals(t, res, http.StatusGone, "Status code mismatch") - - var resetToken database.Token - var account database.Account - testutils.MustExec(t, testutils.DB.Where("value = ?", "MivFxYiSMMA4An9dP24DNQ==").First(&resetToken), "failed to find reset_token") - testutils.MustExec(t, testutils.DB.Where("id = ?", a.ID).First(&account), "failed to find account") - assert.Equal(t, a.Password, account.Password, "password should not have been updated") - assert.Equal(t, resetToken.UsedAt, (*time.Time)(nil), "used_at should be nil") - }) - - t.Run("used token", func(t *testing.T) { - defer testutils.ClearData(testutils.DB) - - // Setup - server := MustNewServer(t, &app.App{ - - Clock: clock.NewMock(), - }) - defer server.Close() - - u := testutils.SetupUserData() - a := testutils.SetupAccountData(u, "alice@example.com", "somepassword") - - usedAt := time.Now().Add(time.Hour * -11).UTC() - tok := database.Token{ - UserID: u.ID, - Value: "MivFxYiSMMA4An9dP24DNQ==", - Type: database.TokenTypeResetPassword, - UsedAt: &usedAt, - } - testutils.MustExec(t, testutils.DB.Save(&tok), "preparing token") - testutils.MustExec(t, testutils.DB.Model(&tok).Update("created_at", time.Now().Add(time.Minute*-11)), "Failed to prepare reset_token created_at") - - dat := `{"token": "MivFxYiSMMA4An9dP24DNQ==", "password": "oldpassword"}` - req := testutils.MakeReq(server.URL, "PATCH", "/reset-password", dat) - - // Execute - res := testutils.HTTPDo(t, req) - - // Test - assert.StatusCodeEquals(t, res, http.StatusBadRequest, "Status code mismatch") - - var resetToken database.Token - var account database.Account - testutils.MustExec(t, testutils.DB.Where("value = ?", "MivFxYiSMMA4An9dP24DNQ==").First(&resetToken), "failed to find reset_token") - testutils.MustExec(t, testutils.DB.Where("id = ?", a.ID).First(&account), "failed to find account") - assert.Equal(t, a.Password, account.Password, "password should not have been updated") - - if resetToken.UsedAt.Year() != usedAt.Year() || - resetToken.UsedAt.Month() != usedAt.Month() || - resetToken.UsedAt.Day() != usedAt.Day() || - resetToken.UsedAt.Hour() != usedAt.Hour() || - resetToken.UsedAt.Minute() != usedAt.Minute() || - resetToken.UsedAt.Second() != usedAt.Second() { - t.Errorf("used_at should be %+v but got: %+v", usedAt, resetToken.UsedAt) - } - }) - - t.Run("using wrong type token: email_verification", func(t *testing.T) { - defer testutils.ClearData(testutils.DB) - - // Setup - server := MustNewServer(t, &app.App{ - - Clock: clock.NewMock(), - }) - defer server.Close() - - u := testutils.SetupUserData() - a := testutils.SetupAccountData(u, "alice@example.com", "somepassword") - tok := database.Token{ - UserID: u.ID, - Value: "MivFxYiSMMA4An9dP24DNQ==", - Type: database.TokenTypeEmailVerification, - } - testutils.MustExec(t, testutils.DB.Save(&tok), "Failed to prepare reset_token") - testutils.MustExec(t, testutils.DB.Model(&tok).Update("created_at", time.Now().Add(time.Minute*-11)), "Failed to prepare reset_token created_at") - - dat := `{"token": "MivFxYiSMMA4An9dP24DNQ==", "password": "oldpassword"}` - req := testutils.MakeReq(server.URL, "PATCH", "/reset-password", dat) - - // Execute - res := testutils.HTTPDo(t, req) - - // Test - assert.StatusCodeEquals(t, res, http.StatusBadRequest, "Status code mismatch") - - var resetToken database.Token - var account database.Account - testutils.MustExec(t, testutils.DB.Where("value = ?", "MivFxYiSMMA4An9dP24DNQ==").First(&resetToken), "failed to find reset_token") - testutils.MustExec(t, testutils.DB.Where("id = ?", a.ID).First(&account), "failed to find account") - - assert.Equal(t, a.Password, account.Password, "password should not have been updated") - assert.Equal(t, resetToken.UsedAt, (*time.Time)(nil), "used_at should be nil") - }) -} diff --git a/pkg/server/api/helpers.go b/pkg/server/api/helpers.go deleted file mode 100644 index 60c990fd..00000000 --- a/pkg/server/api/helpers.go +++ /dev/null @@ -1,85 +0,0 @@ -/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd - * - * This file is part of Dnote. - * - * Dnote is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Dnote is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Dnote. If not, see . - */ - -package api - -import ( - "net/http" - "strings" - - "github.com/dnote/dnote/pkg/server/database" - "github.com/jinzhu/gorm" - "github.com/pkg/errors" -) - -func paginate(conn *gorm.DB, page int) *gorm.DB { - limit := 30 - - // Paginate - if page > 0 { - offset := limit * (page - 1) - conn = conn.Offset(offset) - } - - conn = conn.Limit(limit) - - return conn -} - -func getBookIDs(books []database.Book) []int { - ret := []int{} - - for _, book := range books { - ret = append(ret, book.ID) - } - - return ret -} - -func validatePassword(password string) error { - if len(password) < 8 { - return errors.New("Password should be longer than 8 characters") - } - - return nil -} - -func getClientType(r *http.Request) string { - origin := r.Header.Get("Origin") - - if strings.HasPrefix(origin, "moz-extension://") { - return "firefox-extension" - } - - if strings.HasPrefix(origin, "chrome-extension://") { - return "chrome-extension" - } - - userAgent := r.Header.Get("User-Agent") - if strings.HasPrefix(userAgent, "Go-http-client") { - return "cli" - } - - return "web" -} - -// notSupported is the handler for the route that is no longer supported -func (a *API) notSupported(w http.ResponseWriter, r *http.Request) { - http.Error(w, "API version is not supported. Please upgrade your client.", http.StatusGone) - return -} diff --git a/pkg/server/api/notes.go b/pkg/server/api/notes.go deleted file mode 100644 index d87c2ff7..00000000 --- a/pkg/server/api/notes.go +++ /dev/null @@ -1,324 +0,0 @@ -/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd - * - * This file is part of Dnote. - * - * Dnote is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Dnote is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Dnote. If not, see . - */ - -package api - -import ( - "fmt" - "net/http" - "net/url" - "strconv" - "strings" - "time" - - "github.com/dnote/dnote/pkg/server/database" - "github.com/dnote/dnote/pkg/server/handlers" - "github.com/dnote/dnote/pkg/server/helpers" - "github.com/dnote/dnote/pkg/server/operations" - "github.com/dnote/dnote/pkg/server/presenters" - "github.com/gorilla/mux" - "github.com/jinzhu/gorm" - "github.com/pkg/errors" -) - -type ftsParams struct { - HighlightAll bool -} - -func getHeadlineOptions(params *ftsParams) string { - headlineOptions := []string{ - "StartSel=", - "StopSel=", - "ShortWord=0", - } - - if params != nil && params.HighlightAll { - headlineOptions = append(headlineOptions, "HighlightAll=true") - } else { - headlineOptions = append(headlineOptions, "MaxFragments=3, MaxWords=50, MinWords=10") - } - - return strings.Join(headlineOptions, ",") -} - -func selectFTSFields(conn *gorm.DB, search string, params *ftsParams) *gorm.DB { - headlineOpts := getHeadlineOptions(params) - - return conn.Select(` -notes.id, -notes.uuid, -notes.created_at, -notes.updated_at, -notes.book_uuid, -notes.user_id, -notes.added_on, -notes.edited_on, -notes.usn, -notes.deleted, -notes.encrypted, -ts_headline('english_nostop', notes.body, plainto_tsquery('english_nostop', ?), ?) AS body - `, search, headlineOpts) -} - -func respondWithNote(w http.ResponseWriter, note database.Note) { - presentedNote := presenters.PresentNote(note) - - handlers.RespondJSON(w, http.StatusOK, presentedNote) -} - -func parseSearchQuery(q url.Values) string { - searchStr := q.Get("q") - - return escapeSearchQuery(searchStr) -} - -func getNoteBaseQuery(db *gorm.DB, noteUUID string, search string) *gorm.DB { - var conn *gorm.DB - if search != "" { - conn = selectFTSFields(db, search, &ftsParams{HighlightAll: true}) - } else { - conn = db - } - - conn = conn.Where("notes.uuid = ? AND deleted = ?", noteUUID, false) - - return conn -} - -func (a *API) getNote(w http.ResponseWriter, r *http.Request) { - user, _, err := handlers.AuthWithSession(a.App.DB, r, nil) - if err != nil { - handlers.DoError(w, "authenticating", err, http.StatusInternalServerError) - return - } - - vars := mux.Vars(r) - noteUUID := vars["noteUUID"] - - note, ok, err := operations.GetNote(a.App.DB, noteUUID, user) - if !ok { - handlers.RespondNotFound(w) - return - } - if err != nil { - handlers.DoError(w, "finding note", err, http.StatusInternalServerError) - return - } - - respondWithNote(w, note) -} - -/**** getNotesHandler */ - -// GetNotesResponse is a reponse by getNotesHandler -type GetNotesResponse struct { - Notes []presenters.Note `json:"notes"` - Total int `json:"total"` -} - -type dateRange struct { - lower int64 - upper int64 -} - -func (a *API) getNotes(w http.ResponseWriter, r *http.Request) { - user, ok := r.Context().Value(helpers.KeyUser).(database.User) - if !ok { - handlers.DoError(w, "No authenticated user found", nil, http.StatusInternalServerError) - return - } - query := r.URL.Query() - - respondGetNotes(a.App.DB, user.ID, query, w) -} - -func respondGetNotes(db *gorm.DB, userID int, query url.Values, w http.ResponseWriter) { - q, err := parseGetNotesQuery(query) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - conn := getNotesBaseQuery(db, userID, q) - - var total int - if err := conn.Model(database.Note{}).Count(&total).Error; err != nil { - handlers.DoError(w, "counting total", err, http.StatusInternalServerError) - return - } - - notes := []database.Note{} - if total != 0 { - conn = orderGetNotes(conn) - conn = database.PreloadNote(conn) - conn = paginate(conn, q.Page) - - if err := conn.Find(¬es).Error; err != nil { - handlers.DoError(w, "finding notes", err, http.StatusInternalServerError) - return - } - } - - response := GetNotesResponse{ - Notes: presenters.PresentNotes(notes), - Total: total, - } - handlers.RespondJSON(w, http.StatusOK, response) -} - -type getNotesQuery struct { - Year int - Month int - Page int - Books []string - Search string - Encrypted bool -} - -func parseGetNotesQuery(q url.Values) (getNotesQuery, error) { - yearStr := q.Get("year") - monthStr := q.Get("month") - books := q["book"] - pageStr := q.Get("page") - encryptedStr := q.Get("encrypted") - - fmt.Println("books", books) - - var page int - if len(pageStr) > 0 { - p, err := strconv.Atoi(pageStr) - if err != nil { - return getNotesQuery{}, errors.Errorf("invalid page %s", pageStr) - } - if p < 1 { - return getNotesQuery{}, errors.Errorf("invalid page %s", pageStr) - } - - page = p - } else { - page = 1 - } - - var year int - if len(yearStr) > 0 { - y, err := strconv.Atoi(yearStr) - if err != nil { - return getNotesQuery{}, errors.Errorf("invalid year %s", yearStr) - } - - year = y - } - - var month int - if len(monthStr) > 0 { - m, err := strconv.Atoi(monthStr) - if err != nil { - return getNotesQuery{}, errors.Errorf("invalid month %s", monthStr) - } - if m < 1 || m > 12 { - return getNotesQuery{}, errors.Errorf("invalid month %s", monthStr) - } - - month = m - } - - var encrypted bool - if strings.ToLower(encryptedStr) == "true" { - encrypted = true - } else { - encrypted = false - } - - ret := getNotesQuery{ - Year: year, - Month: month, - Page: page, - Search: parseSearchQuery(q), - Books: books, - Encrypted: encrypted, - } - - return ret, nil -} - -func getDateBounds(year, month int) (int64, int64) { - var yearUpperbound, monthUpperbound int - - if month == 12 { - monthUpperbound = 1 - yearUpperbound = year + 1 - } else { - monthUpperbound = month + 1 - yearUpperbound = year - } - - lower := time.Date(year, time.Month(month), 1, 0, 0, 0, 0, time.UTC).UnixNano() - upper := time.Date(yearUpperbound, time.Month(monthUpperbound), 1, 0, 0, 0, 0, time.UTC).UnixNano() - - return lower, upper -} - -func getNotesBaseQuery(db *gorm.DB, userID int, q getNotesQuery) *gorm.DB { - conn := db.Where( - "notes.user_id = ? AND notes.deleted = ? AND notes.encrypted = ?", - userID, false, q.Encrypted, - ) - - if q.Search != "" { - conn = selectFTSFields(conn, q.Search, nil) - conn = conn.Where("tsv @@ plainto_tsquery('english_nostop', ?)", q.Search) - } - - if len(q.Books) > 0 { - conn = conn.Joins("INNER JOIN books ON books.uuid = notes.book_uuid"). - Where("books.label in (?)", q.Books) - } - - if q.Year != 0 || q.Month != 0 { - dateLowerbound, dateUpperbound := getDateBounds(q.Year, q.Month) - conn = conn.Where("notes.added_on >= ? AND notes.added_on < ?", dateLowerbound, dateUpperbound) - } - - return conn -} - -func orderGetNotes(conn *gorm.DB) *gorm.DB { - return conn.Order("notes.updated_at DESC, notes.id DESC") -} - -// escapeSearchQuery escapes the query for full text search -func escapeSearchQuery(searchQuery string) string { - return strings.Join(strings.Fields(searchQuery), "&") -} - -func (a *API) legacyGetNotes(w http.ResponseWriter, r *http.Request) { - user, ok := r.Context().Value(helpers.KeyUser).(database.User) - if !ok { - handlers.DoError(w, "No authenticated user found", nil, http.StatusInternalServerError) - return - } - - var notes []database.Note - if err := a.App.DB.Where("user_id = ? AND encrypted = true", user.ID).Find(¬es).Error; err != nil { - handlers.DoError(w, "finding notes", err, http.StatusInternalServerError) - return - } - - presented := presenters.PresentNotes(notes) - handlers.RespondJSON(w, http.StatusOK, presented) -} diff --git a/pkg/server/api/notes_test.go b/pkg/server/api/notes_test.go deleted file mode 100644 index c47caf4e..00000000 --- a/pkg/server/api/notes_test.go +++ /dev/null @@ -1,357 +0,0 @@ -/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd - * - * This file is part of Dnote. - * - * Dnote is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Dnote is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Dnote. If not, see . - */ - -package api - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - "testing" - "time" - - "github.com/dnote/dnote/pkg/assert" - "github.com/dnote/dnote/pkg/clock" - "github.com/dnote/dnote/pkg/server/app" - "github.com/dnote/dnote/pkg/server/database" - "github.com/dnote/dnote/pkg/server/presenters" - "github.com/dnote/dnote/pkg/server/testutils" - "github.com/pkg/errors" -) - -func getExpectedNotePayload(n database.Note, b database.Book, u database.User) presenters.Note { - return presenters.Note{ - UUID: n.UUID, - CreatedAt: n.CreatedAt, - UpdatedAt: n.UpdatedAt, - Body: n.Body, - AddedOn: n.AddedOn, - Public: n.Public, - USN: n.USN, - Book: presenters.NoteBook{ - UUID: b.UUID, - Label: b.Label, - }, - User: presenters.NoteUser{ - UUID: u.UUID, - }, - } -} - -func TestGetNotes(t *testing.T) { - defer testutils.ClearData(testutils.DB) - - // Setup - server := MustNewServer(t, &app.App{ - Clock: clock.NewMock(), - }) - defer server.Close() - - user := testutils.SetupUserData() - anotherUser := testutils.SetupUserData() - - b1 := database.Book{ - UserID: user.ID, - Label: "js", - } - testutils.MustExec(t, testutils.DB.Save(&b1), "preparing b1") - b2 := database.Book{ - UserID: user.ID, - Label: "css", - } - testutils.MustExec(t, testutils.DB.Save(&b2), "preparing b2") - b3 := database.Book{ - UserID: anotherUser.ID, - Label: "css", - } - testutils.MustExec(t, testutils.DB.Save(&b3), "preparing b3") - - n1 := database.Note{ - UserID: user.ID, - BookUUID: b1.UUID, - Body: "n1 content", - USN: 11, - Deleted: false, - AddedOn: time.Date(2018, time.August, 10, 23, 0, 0, 0, time.UTC).UnixNano(), - } - testutils.MustExec(t, testutils.DB.Save(&n1), "preparing n1") - n2 := database.Note{ - UserID: user.ID, - BookUUID: b1.UUID, - Body: "n2 content", - USN: 14, - Deleted: false, - AddedOn: time.Date(2018, time.August, 11, 22, 0, 0, 0, time.UTC).UnixNano(), - } - testutils.MustExec(t, testutils.DB.Save(&n2), "preparing n2") - n3 := database.Note{ - UserID: user.ID, - BookUUID: b1.UUID, - Body: "n3 content", - USN: 17, - Deleted: false, - AddedOn: time.Date(2017, time.January, 10, 23, 0, 0, 0, time.UTC).UnixNano(), - } - testutils.MustExec(t, testutils.DB.Save(&n3), "preparing n3") - n4 := database.Note{ - UserID: user.ID, - BookUUID: b2.UUID, - Body: "n4 content", - USN: 18, - Deleted: false, - AddedOn: time.Date(2018, time.September, 10, 23, 0, 0, 0, time.UTC).UnixNano(), - } - testutils.MustExec(t, testutils.DB.Save(&n4), "preparing n4") - n5 := database.Note{ - UserID: anotherUser.ID, - BookUUID: b3.UUID, - Body: "n5 content", - USN: 19, - Deleted: false, - AddedOn: time.Date(2018, time.August, 10, 23, 0, 0, 0, time.UTC).UnixNano(), - } - testutils.MustExec(t, testutils.DB.Save(&n5), "preparing n5") - n6 := database.Note{ - UserID: user.ID, - BookUUID: b1.UUID, - Body: "", - USN: 11, - Deleted: true, - AddedOn: time.Date(2018, time.August, 10, 23, 0, 0, 0, time.UTC).UnixNano(), - } - testutils.MustExec(t, testutils.DB.Save(&n6), "preparing n6") - - // Execute - req := testutils.MakeReq(server.URL, "GET", "/notes?year=2018&month=8", "") - res := testutils.HTTPAuthDo(t, req, user) - - // Test - assert.StatusCodeEquals(t, res, http.StatusOK, "") - - var payload GetNotesResponse - if err := json.NewDecoder(res.Body).Decode(&payload); err != nil { - t.Fatal(errors.Wrap(err, "decoding payload")) - } - - var n2Record, n1Record database.Note - testutils.MustExec(t, testutils.DB.Where("uuid = ?", n2.UUID).First(&n2Record), "finding n2Record") - testutils.MustExec(t, testutils.DB.Where("uuid = ?", n1.UUID).First(&n1Record), "finding n1Record") - - expected := GetNotesResponse{ - Notes: []presenters.Note{ - getExpectedNotePayload(n2Record, b1, user), - getExpectedNotePayload(n1Record, b1, user), - }, - Total: 2, - } - - assert.DeepEqual(t, payload, expected, "payload mismatch") -} - -func TestGetNote(t *testing.T) { - defer testutils.ClearData(testutils.DB) - - // Setup - server := MustNewServer(t, &app.App{ - Clock: clock.NewMock(), - }) - defer server.Close() - - user := testutils.SetupUserData() - anotherUser := testutils.SetupUserData() - - b1 := database.Book{ - UserID: user.ID, - Label: "js", - } - testutils.MustExec(t, testutils.DB.Save(&b1), "preparing b1") - - privateNote := database.Note{ - UserID: user.ID, - BookUUID: b1.UUID, - Body: "privateNote content", - Public: false, - } - testutils.MustExec(t, testutils.DB.Save(&privateNote), "preparing privateNote") - publicNote := database.Note{ - UserID: user.ID, - BookUUID: b1.UUID, - Body: "publicNote content", - Public: true, - } - testutils.MustExec(t, testutils.DB.Save(&publicNote), "preparing publicNote") - deletedNote := database.Note{ - UserID: user.ID, - BookUUID: b1.UUID, - Deleted: true, - } - testutils.MustExec(t, testutils.DB.Save(&deletedNote), "preparing publicNote") - - t.Run("owner accessing private note", func(t *testing.T) { - // Execute - url := fmt.Sprintf("/notes/%s", privateNote.UUID) - req := testutils.MakeReq(server.URL, "GET", url, "") - res := testutils.HTTPAuthDo(t, req, user) - - // Test - assert.StatusCodeEquals(t, res, http.StatusOK, "") - - var payload presenters.Note - if err := json.NewDecoder(res.Body).Decode(&payload); err != nil { - t.Fatal(errors.Wrap(err, "decoding payload")) - } - - var n1Record database.Note - testutils.MustExec(t, testutils.DB.Where("uuid = ?", privateNote.UUID).First(&n1Record), "finding n1Record") - - expected := getExpectedNotePayload(n1Record, b1, user) - assert.DeepEqual(t, payload, expected, "payload mismatch") - }) - - t.Run("owner accessing public note", func(t *testing.T) { - // Execute - url := fmt.Sprintf("/notes/%s", publicNote.UUID) - req := testutils.MakeReq(server.URL, "GET", url, "") - res := testutils.HTTPAuthDo(t, req, user) - - // Test - assert.StatusCodeEquals(t, res, http.StatusOK, "") - - var payload presenters.Note - if err := json.NewDecoder(res.Body).Decode(&payload); err != nil { - t.Fatal(errors.Wrap(err, "decoding payload")) - } - - var n2Record database.Note - testutils.MustExec(t, testutils.DB.Where("uuid = ?", publicNote.UUID).First(&n2Record), "finding n2Record") - - expected := getExpectedNotePayload(n2Record, b1, user) - assert.DeepEqual(t, payload, expected, "payload mismatch") - }) - - t.Run("non-owner accessing public note", func(t *testing.T) { - // Execute - url := fmt.Sprintf("/notes/%s", publicNote.UUID) - req := testutils.MakeReq(server.URL, "GET", url, "") - res := testutils.HTTPAuthDo(t, req, anotherUser) - - // Test - assert.StatusCodeEquals(t, res, http.StatusOK, "") - - var payload presenters.Note - if err := json.NewDecoder(res.Body).Decode(&payload); err != nil { - t.Fatal(errors.Wrap(err, "decoding payload")) - } - - var n2Record database.Note - testutils.MustExec(t, testutils.DB.Where("uuid = ?", publicNote.UUID).First(&n2Record), "finding n2Record") - - expected := getExpectedNotePayload(n2Record, b1, user) - assert.DeepEqual(t, payload, expected, "payload mismatch") - }) - - t.Run("non-owner accessing private note", func(t *testing.T) { - // Execute - url := fmt.Sprintf("/notes/%s", privateNote.UUID) - req := testutils.MakeReq(server.URL, "GET", url, "") - res := testutils.HTTPAuthDo(t, req, anotherUser) - - // Test - assert.StatusCodeEquals(t, res, http.StatusNotFound, "") - - body, err := ioutil.ReadAll(res.Body) - if err != nil { - t.Fatal(errors.Wrap(err, "reading body")) - } - - assert.DeepEqual(t, string(body), "not found\n", "payload mismatch") - }) - - t.Run("guest accessing public note", func(t *testing.T) { - // Execute - url := fmt.Sprintf("/notes/%s", publicNote.UUID) - req := testutils.MakeReq(server.URL, "GET", url, "") - res := testutils.HTTPDo(t, req) - - // Test - assert.StatusCodeEquals(t, res, http.StatusOK, "") - - var payload presenters.Note - if err := json.NewDecoder(res.Body).Decode(&payload); err != nil { - t.Fatal(errors.Wrap(err, "decoding payload")) - } - - var n2Record database.Note - testutils.MustExec(t, testutils.DB.Where("uuid = ?", publicNote.UUID).First(&n2Record), "finding n2Record") - - expected := getExpectedNotePayload(n2Record, b1, user) - assert.DeepEqual(t, payload, expected, "payload mismatch") - }) - - t.Run("guest accessing private note", func(t *testing.T) { - // Execute - url := fmt.Sprintf("/notes/%s", privateNote.UUID) - req := testutils.MakeReq(server.URL, "GET", url, "") - res := testutils.HTTPDo(t, req) - - // Test - assert.StatusCodeEquals(t, res, http.StatusNotFound, "") - - body, err := ioutil.ReadAll(res.Body) - if err != nil { - t.Fatal(errors.Wrap(err, "reading body")) - } - - assert.DeepEqual(t, string(body), "not found\n", "payload mismatch") - }) - - t.Run("nonexistent", func(t *testing.T) { - // Execute - url := fmt.Sprintf("/notes/%s", "someRandomString") - req := testutils.MakeReq(server.URL, "GET", url, "") - res := testutils.HTTPAuthDo(t, req, user) - - // Test - assert.StatusCodeEquals(t, res, http.StatusNotFound, "") - - body, err := ioutil.ReadAll(res.Body) - if err != nil { - t.Fatal(errors.Wrap(err, "reading body")) - } - - assert.DeepEqual(t, string(body), "not found\n", "payload mismatch") - }) - - t.Run("deleted", func(t *testing.T) { - // Execute - url := fmt.Sprintf("/notes/%s", deletedNote.UUID) - req := testutils.MakeReq(server.URL, "GET", url, "") - res := testutils.HTTPAuthDo(t, req, user) - - // Test - assert.StatusCodeEquals(t, res, http.StatusNotFound, "") - - body, err := ioutil.ReadAll(res.Body) - if err != nil { - t.Fatal(errors.Wrap(err, "reading body")) - } - - assert.DeepEqual(t, string(body), "not found\n", "payload mismatch") - }) -} diff --git a/pkg/server/api/routes.go b/pkg/server/api/routes.go deleted file mode 100644 index d1b771bf..00000000 --- a/pkg/server/api/routes.go +++ /dev/null @@ -1,116 +0,0 @@ -/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd - * - * This file is part of Dnote. - * - * Dnote is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Dnote is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Dnote. If not, see . - */ - -package api - -import ( - "net/http" - "os" - - "github.com/dnote/dnote/pkg/server/app" - "github.com/dnote/dnote/pkg/server/database" - "github.com/dnote/dnote/pkg/server/handlers" - "github.com/gorilla/mux" - "github.com/pkg/errors" -) - -// API is a web API configuration -type API struct { - App *app.App -} - -// init sets up the application based on the configuration -func (a *API) init() error { - if err := a.App.Validate(); err != nil { - return errors.Wrap(err, "validating the app parameters") - } - - return nil -} - -func applyMiddleware(h http.HandlerFunc, rateLimit bool) http.Handler { - ret := h - ret = handlers.Logging(ret) - - if rateLimit && os.Getenv("GO_ENV") != "TEST" { - ret = handlers.Limit(ret) - } - - return ret -} - -// NewRouter creates and returns a new router -func NewRouter(a *API) (*mux.Router, error) { - if err := a.init(); err != nil { - return nil, errors.Wrap(err, "initializing app") - } - - proOnly := handlers.AuthParams{ProOnly: true} - app := a.App - - var routes = []handlers.Route{ - // internal - {Method: "GET", Pattern: "/health", HandlerFunc: a.checkHealth, RateLimit: false}, - {Method: "GET", Pattern: "/me", HandlerFunc: handlers.Auth(app, a.getMe, nil), RateLimit: true}, - {Method: "POST", Pattern: "/verification-token", HandlerFunc: handlers.Auth(app, a.createVerificationToken, nil), RateLimit: true}, - {Method: "PATCH", Pattern: "/verify-email", HandlerFunc: a.verifyEmail, RateLimit: true}, - {Method: "POST", Pattern: "/reset-token", HandlerFunc: a.createResetToken, RateLimit: true}, - {Method: "PATCH", Pattern: "/reset-password", HandlerFunc: a.resetPassword, RateLimit: true}, - {Method: "PATCH", Pattern: "/account/profile", HandlerFunc: handlers.Auth(app, a.updateProfile, nil), RateLimit: true}, - {Method: "PATCH", Pattern: "/account/password", HandlerFunc: handlers.Auth(app, a.updatePassword, nil), RateLimit: true}, - {Method: "GET", Pattern: "/account/email-preference", HandlerFunc: handlers.TokenAuth(app, a.getEmailPreference, database.TokenTypeEmailPreference, nil), RateLimit: true}, - {Method: "PATCH", Pattern: "/account/email-preference", HandlerFunc: handlers.TokenAuth(app, a.updateEmailPreference, database.TokenTypeEmailPreference, nil), RateLimit: true}, - {Method: "GET", Pattern: "/notes", HandlerFunc: handlers.Auth(app, a.getNotes, nil), RateLimit: false}, - {Method: "GET", Pattern: "/notes/{noteUUID}", HandlerFunc: a.getNote, RateLimit: true}, - {Method: "GET", Pattern: "/calendar", HandlerFunc: handlers.Auth(app, a.getCalendar, nil), RateLimit: true}, - - // v3 - {Method: "GET", Pattern: "/v3/sync/fragment", HandlerFunc: handlers.Cors(handlers.Auth(app, a.GetSyncFragment, &proOnly)), RateLimit: false}, - {Method: "GET", Pattern: "/v3/sync/state", HandlerFunc: handlers.Cors(handlers.Auth(app, a.GetSyncState, &proOnly)), RateLimit: false}, - {Method: "OPTIONS", Pattern: "/v3/books", HandlerFunc: handlers.Cors(a.BooksOptions), RateLimit: true}, - {Method: "GET", Pattern: "/v3/books", HandlerFunc: handlers.Cors(handlers.Auth(app, a.GetBooks, &proOnly)), RateLimit: true}, - {Method: "GET", Pattern: "/v3/books/{bookUUID}", HandlerFunc: handlers.Cors(handlers.Auth(app, a.GetBook, &proOnly)), RateLimit: true}, - {Method: "POST", Pattern: "/v3/books", HandlerFunc: handlers.Cors(handlers.Auth(app, a.CreateBook, &proOnly)), RateLimit: false}, - {Method: "PATCH", Pattern: "/v3/books/{bookUUID}", HandlerFunc: handlers.Cors(handlers.Auth(app, a.UpdateBook, &proOnly)), RateLimit: false}, - {Method: "DELETE", Pattern: "/v3/books/{bookUUID}", HandlerFunc: handlers.Cors(handlers.Auth(app, a.DeleteBook, &proOnly)), RateLimit: false}, - {Method: "OPTIONS", Pattern: "/v3/notes", HandlerFunc: handlers.Cors(a.NotesOptions), RateLimit: true}, - {Method: "POST", Pattern: "/v3/notes", HandlerFunc: handlers.Cors(handlers.Auth(app, a.CreateNote, &proOnly)), RateLimit: false}, - {Method: "PATCH", Pattern: "/v3/notes/{noteUUID}", HandlerFunc: handlers.Auth(app, a.UpdateNote, &proOnly), RateLimit: false}, - {Method: "DELETE", Pattern: "/v3/notes/{noteUUID}", HandlerFunc: handlers.Auth(app, a.DeleteNote, &proOnly), RateLimit: false}, - {Method: "POST", Pattern: "/v3/signin", HandlerFunc: handlers.Cors(a.signin), RateLimit: true}, - {Method: "OPTIONS", Pattern: "/v3/signout", HandlerFunc: handlers.Cors(a.signoutOptions), RateLimit: true}, - {Method: "POST", Pattern: "/v3/signout", HandlerFunc: handlers.Cors(a.signout), RateLimit: true}, - {Method: "POST", Pattern: "/v3/register", HandlerFunc: a.register, RateLimit: true}, - } - - router := mux.NewRouter().StrictSlash(true) - - router.PathPrefix("/v1").Handler(applyMiddleware(handlers.NotSupported, true)) - router.PathPrefix("/v2").Handler(applyMiddleware(handlers.NotSupported, true)) - - for _, route := range routes { - handler := route.HandlerFunc - - router. - Methods(route.Method). - Path(route.Pattern). - Handler(applyMiddleware(handler, route.RateLimit)) - } - - return router, nil -} diff --git a/pkg/server/api/routes_test.go b/pkg/server/api/routes_test.go deleted file mode 100644 index 7b8f5ec2..00000000 --- a/pkg/server/api/routes_test.go +++ /dev/null @@ -1,161 +0,0 @@ -/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd - * - * This file is part of Dnote. - * - * Dnote is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Dnote is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Dnote. If not, see . - */ - -package api - -import ( - "fmt" - "net/http" - "testing" - - "github.com/dnote/dnote/pkg/assert" - "github.com/dnote/dnote/pkg/clock" - "github.com/dnote/dnote/pkg/server/app" - "github.com/dnote/dnote/pkg/server/config" - "github.com/dnote/dnote/pkg/server/mailer" - "github.com/dnote/dnote/pkg/server/testutils" - "github.com/jinzhu/gorm" - "github.com/pkg/errors" -) - -func TestNotSupportedVersions(t *testing.T) { - testCases := []struct { - path string - }{ - // v1 - { - path: "/v1", - }, - { - path: "/v1/foo", - }, - { - path: "/v1/bar/baz", - }, - // v2 - { - path: "/v2", - }, - { - path: "/v2/foo", - }, - { - path: "/v2/bar/baz", - }, - } - - // setup - server := MustNewServer(t, &app.App{ - DB: &gorm.DB{}, - Clock: clock.NewMock(), - }) - defer server.Close() - - for _, tc := range testCases { - t.Run(tc.path, func(t *testing.T) { - // execute - req := testutils.MakeReq(server.URL, "GET", tc.path, "") - res := testutils.HTTPDo(t, req) - - // test - assert.Equal(t, res.StatusCode, http.StatusGone, "status code mismatch") - }) - } -} - -func TestNewRouter_AppValidate(t *testing.T) { - c := config.Load() - - configWithoutWebURL := config.Load() - configWithoutWebURL.WebURL = "" - - testCases := []struct { - app app.App - expectedErr error - }{ - { - app: app.App{ - DB: &gorm.DB{}, - Clock: clock.NewMock(), - EmailTemplates: mailer.Templates{}, - EmailBackend: &testutils.MockEmailbackendImplementation{}, - Config: c, - }, - expectedErr: nil, - }, - { - app: app.App{ - DB: nil, - Clock: clock.NewMock(), - EmailTemplates: mailer.Templates{}, - EmailBackend: &testutils.MockEmailbackendImplementation{}, - Config: c, - }, - expectedErr: app.ErrEmptyDB, - }, - { - app: app.App{ - DB: &gorm.DB{}, - Clock: nil, - EmailTemplates: mailer.Templates{}, - EmailBackend: &testutils.MockEmailbackendImplementation{}, - Config: c, - }, - expectedErr: app.ErrEmptyClock, - }, - { - app: app.App{ - DB: &gorm.DB{}, - Clock: clock.NewMock(), - EmailTemplates: nil, - EmailBackend: &testutils.MockEmailbackendImplementation{}, - Config: c, - }, - expectedErr: app.ErrEmptyEmailTemplates, - }, - { - app: app.App{ - DB: &gorm.DB{}, - Clock: clock.NewMock(), - EmailTemplates: mailer.Templates{}, - EmailBackend: nil, - Config: c, - }, - expectedErr: app.ErrEmptyEmailBackend, - }, - { - app: app.App{ - DB: &gorm.DB{}, - Clock: clock.NewMock(), - EmailTemplates: mailer.Templates{}, - EmailBackend: &testutils.MockEmailbackendImplementation{}, - Config: configWithoutWebURL, - }, - expectedErr: app.ErrEmptyWebURL, - }, - } - - for idx, tc := range testCases { - t.Run(fmt.Sprintf("test case %d", idx), func(t *testing.T) { - api := API{App: &tc.app} - _, err := NewRouter(&api) - - assert.Equal(t, errors.Cause(err), tc.expectedErr, "error mismatch") - }) - } -} diff --git a/pkg/server/api/user.go b/pkg/server/api/user.go deleted file mode 100644 index d4a8ec51..00000000 --- a/pkg/server/api/user.go +++ /dev/null @@ -1,394 +0,0 @@ -/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd - * - * This file is part of Dnote. - * - * Dnote is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Dnote is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Dnote. If not, see . - */ - -package api - -import ( - "encoding/json" - "net/http" - "time" - - "github.com/dnote/dnote/pkg/server/database" - "github.com/dnote/dnote/pkg/server/handlers" - "github.com/dnote/dnote/pkg/server/helpers" - "github.com/dnote/dnote/pkg/server/log" - "github.com/dnote/dnote/pkg/server/mailer" - "github.com/dnote/dnote/pkg/server/presenters" - "github.com/dnote/dnote/pkg/server/session" - "github.com/dnote/dnote/pkg/server/token" - "github.com/jinzhu/gorm" - "github.com/pkg/errors" - "golang.org/x/crypto/bcrypt" -) - -type updateProfilePayload struct { - Email string `json:"email"` - Password string `json:"password"` -} - -// updateProfile updates user -func (a *API) updateProfile(w http.ResponseWriter, r *http.Request) { - user, ok := r.Context().Value(helpers.KeyUser).(database.User) - if !ok { - handlers.DoError(w, "No authenticated user found", nil, http.StatusInternalServerError) - return - } - - var account database.Account - if err := a.App.DB.Where("user_id = ?", user.ID).First(&account).Error; err != nil { - handlers.DoError(w, "getting account", nil, http.StatusInternalServerError) - return - } - - var params updateProfilePayload - err := json.NewDecoder(r.Body).Decode(¶ms) - if err != nil { - http.Error(w, errors.Wrap(err, "invalid params").Error(), http.StatusBadRequest) - return - } - - password := []byte(params.Password) - if err := bcrypt.CompareHashAndPassword([]byte(account.Password.String), password); err != nil { - log.WithFields(log.Fields{ - "user_id": user.ID, - }).Warn("invalid email update attempt") - http.Error(w, "Wrong password", http.StatusUnauthorized) - return - } - - // Validate - if len(params.Email) > 60 { - http.Error(w, "Email is too long", http.StatusBadRequest) - return - } - - tx := a.App.DB.Begin() - if err := tx.Save(&user).Error; err != nil { - tx.Rollback() - handlers.DoError(w, "saving user", err, http.StatusInternalServerError) - return - } - - // check if email was changed - if params.Email != account.Email.String { - account.EmailVerified = false - } - account.Email.String = params.Email - - if err := tx.Save(&account).Error; err != nil { - tx.Rollback() - handlers.DoError(w, "saving account", err, http.StatusInternalServerError) - return - } - - tx.Commit() - - a.respondWithSession(a.App.DB, w, user.ID, http.StatusOK) -} - -type updateEmailPayload struct { - NewEmail string `json:"new_email"` - NewCipherKeyEnc string `json:"new_cipher_key_enc"` - OldAuthKey string `json:"old_auth_key"` - NewAuthKey string `json:"new_auth_key"` -} - -func respondWithCalendar(db *gorm.DB, w http.ResponseWriter, userID int) { - rows, err := db.Table("notes").Select("COUNT(id), date(to_timestamp(added_on/1000000000)) AS added_date"). - Where("user_id = ?", userID). - Group("added_date"). - Order("added_date DESC").Rows() - - if err != nil { - handlers.DoError(w, "Failed to count lessons", err, http.StatusInternalServerError) - return - } - - payload := map[string]int{} - - for rows.Next() { - var count int - var d time.Time - - if err := rows.Scan(&count, &d); err != nil { - handlers.DoError(w, "counting notes", err, http.StatusInternalServerError) - } - payload[d.Format("2006-1-2")] = count - } - - handlers.RespondJSON(w, http.StatusOK, payload) -} - -func (a *API) getCalendar(w http.ResponseWriter, r *http.Request) { - user, ok := r.Context().Value(helpers.KeyUser).(database.User) - if !ok { - handlers.DoError(w, "No authenticated user found", nil, http.StatusInternalServerError) - return - } - - respondWithCalendar(a.App.DB, w, user.ID) -} - -func (a *API) createVerificationToken(w http.ResponseWriter, r *http.Request) { - user, ok := r.Context().Value(helpers.KeyUser).(database.User) - if !ok { - handlers.DoError(w, "No authenticated user found", nil, http.StatusInternalServerError) - return - } - - var account database.Account - err := a.App.DB.Where("user_id = ?", user.ID).First(&account).Error - if err != nil { - handlers.DoError(w, "finding account", err, http.StatusInternalServerError) - return - } - - if account.EmailVerified { - http.Error(w, "Email already verified", http.StatusGone) - return - } - if account.Email.String == "" { - http.Error(w, "Email not set", http.StatusUnprocessableEntity) - return - } - - tok, err := token.Create(a.App.DB, account.UserID, database.TokenTypeEmailVerification) - if err != nil { - handlers.DoError(w, "saving token", err, http.StatusInternalServerError) - return - } - - if err := a.App.SendVerificationEmail(account.Email.String, tok.Value); err != nil { - if errors.Cause(err) == mailer.ErrSMTPNotConfigured { - handlers.RespondInvalidSMTPConfig(w) - } else { - handlers.DoError(w, errors.Wrap(err, "sending verification email").Error(), nil, http.StatusInternalServerError) - } - - return - } - - w.WriteHeader(http.StatusCreated) -} - -type verifyEmailPayload struct { - Token string `json:"token"` -} - -func (a *API) verifyEmail(w http.ResponseWriter, r *http.Request) { - var params verifyEmailPayload - if err := json.NewDecoder(r.Body).Decode(¶ms); err != nil { - handlers.DoError(w, "decoding payload", err, http.StatusInternalServerError) - return - } - - var token database.Token - if err := a.App.DB. - Where("value = ? AND type = ?", params.Token, database.TokenTypeEmailVerification). - First(&token).Error; err != nil { - http.Error(w, "invalid token", http.StatusBadRequest) - return - } - - if token.UsedAt != nil { - http.Error(w, "invalid token", http.StatusBadRequest) - return - } - - // Expire after ttl - if time.Since(token.CreatedAt).Minutes() > 30 { - http.Error(w, "This link has been expired. Please request a new link.", http.StatusGone) - return - } - - var account database.Account - if err := a.App.DB.Where("user_id = ?", token.UserID).First(&account).Error; err != nil { - handlers.DoError(w, "finding account", err, http.StatusInternalServerError) - return - } - if account.EmailVerified { - http.Error(w, "Already verified", http.StatusConflict) - return - } - - tx := a.App.DB.Begin() - account.EmailVerified = true - if err := tx.Save(&account).Error; err != nil { - tx.Rollback() - handlers.DoError(w, "updating email_verified", err, http.StatusInternalServerError) - return - } - if err := tx.Model(&token).Update("used_at", time.Now()).Error; err != nil { - tx.Rollback() - handlers.DoError(w, "updating reset token", err, http.StatusInternalServerError) - return - } - tx.Commit() - - var user database.User - if err := a.App.DB.Where("id = ?", token.UserID).First(&user).Error; err != nil { - handlers.DoError(w, "finding user", err, http.StatusInternalServerError) - return - } - - s := session.New(user, account) - handlers.RespondJSON(w, http.StatusOK, s) -} - -type emailPreferernceParams struct { - InactiveReminder *bool `json:"inactive_reminder"` - ProductUpdate *bool `json:"product_update"` -} - -func (p emailPreferernceParams) getInactiveReminder() bool { - if p.InactiveReminder == nil { - return false - } - - return *p.InactiveReminder -} - -func (p emailPreferernceParams) getProductUpdate() bool { - if p.ProductUpdate == nil { - return false - } - - return *p.ProductUpdate -} - -func (a *API) updateEmailPreference(w http.ResponseWriter, r *http.Request) { - user, ok := r.Context().Value(helpers.KeyUser).(database.User) - if !ok { - handlers.DoError(w, "No authenticated user found", nil, http.StatusInternalServerError) - return - } - - var params emailPreferernceParams - if err := json.NewDecoder(r.Body).Decode(¶ms); err != nil { - handlers.DoError(w, "decoding payload", err, http.StatusInternalServerError) - return - } - - var pref database.EmailPreference - if err := a.App.DB.Where(database.EmailPreference{UserID: user.ID}).FirstOrCreate(&pref).Error; err != nil { - handlers.DoError(w, "finding pref", err, http.StatusInternalServerError) - return - } - - tx := a.App.DB.Begin() - - if params.InactiveReminder != nil { - pref.InactiveReminder = params.getInactiveReminder() - } - if params.ProductUpdate != nil { - pref.ProductUpdate = params.getProductUpdate() - } - - if err := tx.Save(&pref).Error; err != nil { - tx.Rollback() - handlers.DoError(w, "saving pref", err, http.StatusInternalServerError) - return - } - - token, ok := r.Context().Value(helpers.KeyToken).(database.Token) - if ok { - // Mark token as used if the user was authenticated by token - if err := tx.Model(&token).Update("used_at", time.Now()).Error; err != nil { - tx.Rollback() - handlers.DoError(w, "updating reset token", err, http.StatusInternalServerError) - return - } - } - - tx.Commit() - - handlers.RespondJSON(w, http.StatusOK, pref) -} - -func (a *API) getEmailPreference(w http.ResponseWriter, r *http.Request) { - user, ok := r.Context().Value(helpers.KeyUser).(database.User) - if !ok { - handlers.DoError(w, "No authenticated user found", nil, http.StatusInternalServerError) - return - } - - var pref database.EmailPreference - if err := a.App.DB.Where(database.EmailPreference{UserID: user.ID}).First(&pref).Error; err != nil { - handlers.DoError(w, "finding pref", err, http.StatusInternalServerError) - return - } - - presented := presenters.PresentEmailPreference(pref) - handlers.RespondJSON(w, http.StatusOK, presented) -} - -type updatePasswordPayload struct { - OldPassword string `json:"old_password"` - NewPassword string `json:"new_password"` -} - -func (a *API) updatePassword(w http.ResponseWriter, r *http.Request) { - user, ok := r.Context().Value(helpers.KeyUser).(database.User) - if !ok { - handlers.DoError(w, "No authenticated user found", nil, http.StatusInternalServerError) - return - } - - var params updatePasswordPayload - if err := json.NewDecoder(r.Body).Decode(¶ms); err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - if params.OldPassword == "" || params.NewPassword == "" { - http.Error(w, "invalid params", http.StatusBadRequest) - return - } - - var account database.Account - if err := a.App.DB.Where("user_id = ?", user.ID).First(&account).Error; err != nil { - handlers.DoError(w, "getting account", nil, http.StatusInternalServerError) - return - } - - password := []byte(params.OldPassword) - if err := bcrypt.CompareHashAndPassword([]byte(account.Password.String), password); err != nil { - log.WithFields(log.Fields{ - "user_id": user.ID, - }).Warn("invalid password update attempt") - http.Error(w, "Wrong password", http.StatusUnauthorized) - return - } - - if err := validatePassword(params.NewPassword); err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - hashedNewPassword, err := bcrypt.GenerateFromPassword([]byte(params.NewPassword), bcrypt.DefaultCost) - if err != nil { - http.Error(w, errors.Wrap(err, "hashing password").Error(), http.StatusInternalServerError) - return - } - - if err := a.App.DB.Model(&account).Update("password", string(hashedNewPassword)).Error; err != nil { - http.Error(w, errors.Wrap(err, "updating password").Error(), http.StatusInternalServerError) - return - } - - w.WriteHeader(http.StatusOK) -} diff --git a/pkg/server/api/user_test.go b/pkg/server/api/user_test.go deleted file mode 100644 index 95df0179..00000000 --- a/pkg/server/api/user_test.go +++ /dev/null @@ -1,691 +0,0 @@ -/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd - * - * This file is part of Dnote. - * - * Dnote is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Dnote is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Dnote. If not, see . - */ - -package api - -import ( - "encoding/json" - "fmt" - "net/http" - "testing" - "time" - - "github.com/dnote/dnote/pkg/assert" - "github.com/dnote/dnote/pkg/clock" - "github.com/dnote/dnote/pkg/server/app" - "github.com/dnote/dnote/pkg/server/database" - "github.com/dnote/dnote/pkg/server/presenters" - "github.com/dnote/dnote/pkg/server/testutils" - "github.com/pkg/errors" - "golang.org/x/crypto/bcrypt" -) - -func TestUpdatePassword(t *testing.T) { - t.Run("success", func(t *testing.T) { - defer testutils.ClearData(testutils.DB) - - // Setup - server := MustNewServer(t, &app.App{ - Clock: clock.NewMock(), - }) - defer server.Close() - - user := testutils.SetupUserData() - testutils.SetupAccountData(user, "alice@example.com", "oldpassword") - - // Execute - dat := `{"old_password": "oldpassword", "new_password": "newpassword"}` - req := testutils.MakeReq(server.URL, "PATCH", "/account/password", dat) - res := testutils.HTTPAuthDo(t, req, user) - - // Test - assert.StatusCodeEquals(t, res, http.StatusOK, "Status code mismsatch") - - var account database.Account - testutils.MustExec(t, testutils.DB.Where("user_id = ?", user.ID).First(&account), "finding account") - - passwordErr := bcrypt.CompareHashAndPassword([]byte(account.Password.String), []byte("newpassword")) - assert.Equal(t, passwordErr, nil, "Password mismatch") - }) - - t.Run("old password mismatch", func(t *testing.T) { - defer testutils.ClearData(testutils.DB) - - // Setup - server := MustNewServer(t, &app.App{ - - Clock: clock.NewMock(), - }) - defer server.Close() - - u := testutils.SetupUserData() - a := testutils.SetupAccountData(u, "alice@example.com", "oldpassword") - - // Execute - dat := `{"old_password": "randompassword", "new_password": "newpassword"}` - req := testutils.MakeReq(server.URL, "PATCH", "/account/password", dat) - res := testutils.HTTPAuthDo(t, req, u) - - // Test - assert.StatusCodeEquals(t, res, http.StatusUnauthorized, "Status code mismsatch") - - var account database.Account - testutils.MustExec(t, testutils.DB.Where("user_id = ?", u.ID).First(&account), "finding account") - assert.Equal(t, a.Password.String, account.Password.String, "password should not have been updated") - }) - - t.Run("password too short", func(t *testing.T) { - defer testutils.ClearData(testutils.DB) - - // Setup - server := MustNewServer(t, &app.App{ - - Clock: clock.NewMock(), - }) - defer server.Close() - - u := testutils.SetupUserData() - a := testutils.SetupAccountData(u, "alice@example.com", "oldpassword") - - // Execute - dat := `{"old_password": "oldpassword", "new_password": "a"}` - req := testutils.MakeReq(server.URL, "PATCH", "/account/password", dat) - res := testutils.HTTPAuthDo(t, req, u) - - // Test - assert.StatusCodeEquals(t, res, http.StatusBadRequest, "Status code mismsatch") - - var account database.Account - testutils.MustExec(t, testutils.DB.Where("user_id = ?", u.ID).First(&account), "finding account") - assert.Equal(t, a.Password.String, account.Password.String, "password should not have been updated") - }) -} - -func TestCreateVerificationToken(t *testing.T) { - t.Run("success", func(t *testing.T) { - defer testutils.ClearData(testutils.DB) - - // Setup - emailBackend := testutils.MockEmailbackendImplementation{} - server := MustNewServer(t, &app.App{ - Clock: clock.NewMock(), - EmailBackend: &emailBackend, - }) - defer server.Close() - - user := testutils.SetupUserData() - testutils.SetupAccountData(user, "alice@example.com", "pass1234") - - // Execute - req := testutils.MakeReq(server.URL, "POST", "/verification-token", "") - res := testutils.HTTPAuthDo(t, req, user) - - // Test - assert.StatusCodeEquals(t, res, http.StatusCreated, "status code mismatch") - - var account database.Account - var token database.Token - var tokenCount int - testutils.MustExec(t, testutils.DB.Where("user_id = ?", user.ID).First(&account), "finding account") - testutils.MustExec(t, testutils.DB.Where("user_id = ? AND type = ?", user.ID, database.TokenTypeEmailVerification).First(&token), "finding token") - testutils.MustExec(t, testutils.DB.Model(&database.Token{}).Count(&tokenCount), "counting token") - - assert.Equal(t, account.EmailVerified, false, "email_verified should not have been updated") - assert.NotEqual(t, token.Value, "", "token Value mismatch") - assert.Equal(t, tokenCount, 1, "token count mismatch") - assert.Equal(t, token.UsedAt, (*time.Time)(nil), "token UsedAt mismatch") - assert.Equal(t, len(emailBackend.Emails), 1, "email queue count mismatch") - }) - - t.Run("already verified", func(t *testing.T) { - - defer testutils.ClearData(testutils.DB) - - // Setup - server := MustNewServer(t, &app.App{ - - Clock: clock.NewMock(), - }) - defer server.Close() - - user := testutils.SetupUserData() - a := testutils.SetupAccountData(user, "alice@example.com", "pass1234") - a.EmailVerified = true - testutils.MustExec(t, testutils.DB.Save(&a), "preparing account") - - // Execute - req := testutils.MakeReq(server.URL, "POST", "/verification-token", "") - res := testutils.HTTPAuthDo(t, req, user) - - // Test - assert.StatusCodeEquals(t, res, http.StatusGone, "Status code mismatch") - - var account database.Account - var tokenCount int - testutils.MustExec(t, testutils.DB.Where("user_id = ?", user.ID).First(&account), "finding account") - testutils.MustExec(t, testutils.DB.Model(&database.Token{}).Count(&tokenCount), "counting token") - - assert.Equal(t, account.EmailVerified, true, "email_verified should not have been updated") - assert.Equal(t, tokenCount, 0, "token count mismatch") - }) -} - -func TestVerifyEmail(t *testing.T) { - t.Run("success", func(t *testing.T) { - - defer testutils.ClearData(testutils.DB) - - // Setup - server := MustNewServer(t, &app.App{ - Clock: clock.NewMock(), - }) - defer server.Close() - - user := testutils.SetupUserData() - testutils.SetupAccountData(user, "alice@example.com", "pass1234") - tok := database.Token{ - UserID: user.ID, - Type: database.TokenTypeEmailVerification, - Value: "someTokenValue", - } - testutils.MustExec(t, testutils.DB.Save(&tok), "preparing token") - - dat := `{"token": "someTokenValue"}` - req := testutils.MakeReq(server.URL, "PATCH", "/verify-email", dat) - - // Execute - res := testutils.HTTPAuthDo(t, req, user) - - // Test - assert.StatusCodeEquals(t, res, http.StatusOK, "Status code mismatch") - - var account database.Account - var token database.Token - var tokenCount int - testutils.MustExec(t, testutils.DB.Where("user_id = ?", user.ID).First(&account), "finding account") - testutils.MustExec(t, testutils.DB.Where("user_id = ? AND type = ?", user.ID, database.TokenTypeEmailVerification).First(&token), "finding token") - testutils.MustExec(t, testutils.DB.Model(&database.Token{}).Count(&tokenCount), "counting token") - - assert.Equal(t, account.EmailVerified, true, "email_verified mismatch") - assert.NotEqual(t, token.Value, "", "token value should not have been updated") - assert.Equal(t, tokenCount, 1, "token count mismatch") - assert.NotEqual(t, token.UsedAt, (*time.Time)(nil), "token should have been used") - }) - - t.Run("used token", func(t *testing.T) { - - defer testutils.ClearData(testutils.DB) - - // Setup - server := MustNewServer(t, &app.App{ - Clock: clock.NewMock(), - }) - defer server.Close() - - user := testutils.SetupUserData() - testutils.SetupAccountData(user, "alice@example.com", "pass1234") - - usedAt := time.Now().Add(time.Hour * -11).UTC() - tok := database.Token{ - UserID: user.ID, - Type: database.TokenTypeEmailVerification, - Value: "someTokenValue", - UsedAt: &usedAt, - } - testutils.MustExec(t, testutils.DB.Save(&tok), "preparing token") - - dat := `{"token": "someTokenValue"}` - req := testutils.MakeReq(server.URL, "PATCH", "/verify-email", dat) - - // Execute - res := testutils.HTTPAuthDo(t, req, user) - - // Test - assert.StatusCodeEquals(t, res, http.StatusBadRequest, "") - - var account database.Account - var token database.Token - var tokenCount int - testutils.MustExec(t, testutils.DB.Where("user_id = ?", user.ID).First(&account), "finding account") - testutils.MustExec(t, testutils.DB.Where("user_id = ? AND type = ?", user.ID, database.TokenTypeEmailVerification).First(&token), "finding token") - testutils.MustExec(t, testutils.DB.Model(&database.Token{}).Count(&tokenCount), "counting token") - - assert.Equal(t, account.EmailVerified, false, "email_verified mismatch") - assert.NotEqual(t, token.UsedAt, nil, "token used_at mismatch") - assert.Equal(t, tokenCount, 1, "token count mismatch") - assert.NotEqual(t, token.UsedAt, (*time.Time)(nil), "token should have been used") - }) - - t.Run("expired token", func(t *testing.T) { - - defer testutils.ClearData(testutils.DB) - - // Setup - server := MustNewServer(t, &app.App{ - Clock: clock.NewMock(), - }) - defer server.Close() - - user := testutils.SetupUserData() - testutils.SetupAccountData(user, "alice@example.com", "pass1234") - - tok := database.Token{ - UserID: user.ID, - Type: database.TokenTypeEmailVerification, - Value: "someTokenValue", - } - testutils.MustExec(t, testutils.DB.Save(&tok), "preparing token") - testutils.MustExec(t, testutils.DB.Model(&tok).Update("created_at", time.Now().Add(time.Minute*-31)), "Failed to prepare token created_at") - - dat := `{"token": "someTokenValue"}` - req := testutils.MakeReq(server.URL, "PATCH", "/verify-email", dat) - - // Execute - res := testutils.HTTPAuthDo(t, req, user) - - // Test - assert.StatusCodeEquals(t, res, http.StatusGone, "") - - var account database.Account - var token database.Token - var tokenCount int - testutils.MustExec(t, testutils.DB.Where("user_id = ?", user.ID).First(&account), "finding account") - testutils.MustExec(t, testutils.DB.Where("user_id = ? AND type = ?", user.ID, database.TokenTypeEmailVerification).First(&token), "finding token") - testutils.MustExec(t, testutils.DB.Model(&database.Token{}).Count(&tokenCount), "counting token") - - assert.Equal(t, account.EmailVerified, false, "email_verified mismatch") - assert.Equal(t, tokenCount, 1, "token count mismatch") - assert.Equal(t, token.UsedAt, (*time.Time)(nil), "token should have not been used") - }) - - t.Run("already verified", func(t *testing.T) { - - defer testutils.ClearData(testutils.DB) - - // Setup - server := MustNewServer(t, &app.App{ - Clock: clock.NewMock(), - }) - defer server.Close() - - user := testutils.SetupUserData() - a := testutils.SetupAccountData(user, "alice@example.com", "oldpass1234") - a.EmailVerified = true - testutils.MustExec(t, testutils.DB.Save(&a), "preparing account") - - tok := database.Token{ - UserID: user.ID, - Type: database.TokenTypeEmailVerification, - Value: "someTokenValue", - } - testutils.MustExec(t, testutils.DB.Save(&tok), "preparing token") - - dat := `{"token": "someTokenValue"}` - req := testutils.MakeReq(server.URL, "PATCH", "/verify-email", dat) - - // Execute - res := testutils.HTTPAuthDo(t, req, user) - - // Test - assert.StatusCodeEquals(t, res, http.StatusConflict, "") - - var account database.Account - var token database.Token - var tokenCount int - testutils.MustExec(t, testutils.DB.Where("user_id = ?", user.ID).First(&account), "finding account") - testutils.MustExec(t, testutils.DB.Where("user_id = ? AND type = ?", user.ID, database.TokenTypeEmailVerification).First(&token), "finding token") - testutils.MustExec(t, testutils.DB.Model(&database.Token{}).Count(&tokenCount), "counting token") - - assert.Equal(t, account.EmailVerified, true, "email_verified mismatch") - assert.Equal(t, tokenCount, 1, "token count mismatch") - assert.Equal(t, token.UsedAt, (*time.Time)(nil), "token should have not been used") - }) -} - -func TestUpdateEmail(t *testing.T) { - t.Run("success", func(t *testing.T) { - defer testutils.ClearData(testutils.DB) - - // Setup - server := MustNewServer(t, &app.App{ - Clock: clock.NewMock(), - }) - defer server.Close() - - u := testutils.SetupUserData() - a := testutils.SetupAccountData(u, "alice@example.com", "pass1234") - a.EmailVerified = true - testutils.MustExec(t, testutils.DB.Save(&a), "updating email_verified") - - // Execute - dat := `{"email": "alice-new@example.com", "password": "pass1234"}` - req := testutils.MakeReq(server.URL, "PATCH", "/account/profile", dat) - res := testutils.HTTPAuthDo(t, req, u) - - // Test - assert.StatusCodeEquals(t, res, http.StatusOK, "") - - var user database.User - var account database.Account - testutils.MustExec(t, testutils.DB.Where("id = ?", u.ID).First(&user), "finding user") - testutils.MustExec(t, testutils.DB.Where("user_id = ?", u.ID).First(&account), "finding account") - - assert.Equal(t, account.Email.String, "alice-new@example.com", "email mismatch") - assert.Equal(t, account.EmailVerified, false, "EmailVerified mismatch") - }) - - t.Run("password mismatch", func(t *testing.T) { - defer testutils.ClearData(testutils.DB) - - // Setup - server := MustNewServer(t, &app.App{ - Clock: clock.NewMock(), - }) - defer server.Close() - - u := testutils.SetupUserData() - a := testutils.SetupAccountData(u, "alice@example.com", "pass1234") - a.EmailVerified = true - testutils.MustExec(t, testutils.DB.Save(&a), "updating email_verified") - - // Execute - dat := `{"email": "alice-new@example.com", "password": "wrongpassword"}` - req := testutils.MakeReq(server.URL, "PATCH", "/account/profile", dat) - res := testutils.HTTPAuthDo(t, req, u) - - // Test - assert.StatusCodeEquals(t, res, http.StatusUnauthorized, "Status code mismsatch") - - var user database.User - var account database.Account - testutils.MustExec(t, testutils.DB.Where("id = ?", u.ID).First(&user), "finding user") - testutils.MustExec(t, testutils.DB.Where("user_id = ?", u.ID).First(&account), "finding account") - - assert.Equal(t, account.Email.String, "alice@example.com", "email mismatch") - assert.Equal(t, account.EmailVerified, true, "EmailVerified mismatch") - }) -} - -func TestUpdateEmailPreference(t *testing.T) { - t.Run("with login", func(t *testing.T) { - defer testutils.ClearData(testutils.DB) - - // Setup - server := MustNewServer(t, &app.App{ - Clock: clock.NewMock(), - }) - defer server.Close() - - u := testutils.SetupUserData() - testutils.SetupEmailPreferenceData(u, false) - - // Execute - dat := `{"inactive_reminder": true}` - req := testutils.MakeReq(server.URL, "PATCH", "/account/email-preference", dat) - res := testutils.HTTPAuthDo(t, req, u) - - // Test - assert.StatusCodeEquals(t, res, http.StatusOK, "") - - var preference database.EmailPreference - testutils.MustExec(t, testutils.DB.Where("user_id = ?", u.ID).First(&preference), "finding account") - assert.Equal(t, preference.InactiveReminder, true, "preference mismatch") - }) - - t.Run("with an unused token", func(t *testing.T) { - defer testutils.ClearData(testutils.DB) - - // Setup - server := MustNewServer(t, &app.App{ - Clock: clock.NewMock(), - }) - defer server.Close() - - u := testutils.SetupUserData() - testutils.SetupEmailPreferenceData(u, false) - tok := database.Token{ - UserID: u.ID, - Type: database.TokenTypeEmailPreference, - Value: "someTokenValue", - } - testutils.MustExec(t, testutils.DB.Save(&tok), "preparing token") - - // Execute - dat := `{"inactive_reminder": true}` - url := fmt.Sprintf("/account/email-preference?token=%s", "someTokenValue") - req := testutils.MakeReq(server.URL, "PATCH", url, dat) - res := testutils.HTTPDo(t, req) - - // Test - assert.StatusCodeEquals(t, res, http.StatusOK, "") - - var preference database.EmailPreference - var preferenceCount int - var token database.Token - testutils.MustExec(t, testutils.DB.Where("user_id = ?", u.ID).First(&preference), "finding preference") - testutils.MustExec(t, testutils.DB.Model(database.EmailPreference{}).Count(&preferenceCount), "counting preference") - testutils.MustExec(t, testutils.DB.Where("id = ?", tok.ID).First(&token), "failed to find token") - - assert.Equal(t, preferenceCount, 1, "preference count mismatch") - assert.Equal(t, preference.InactiveReminder, true, "email mismatch") - assert.NotEqual(t, token.UsedAt, (*time.Time)(nil), "token should have been used") - }) - - t.Run("with nonexistent token", func(t *testing.T) { - defer testutils.ClearData(testutils.DB) - - // Setup - server := MustNewServer(t, &app.App{ - Clock: clock.NewMock(), - }) - defer server.Close() - - u := testutils.SetupUserData() - testutils.SetupEmailPreferenceData(u, true) - tok := database.Token{ - UserID: u.ID, - Type: database.TokenTypeEmailPreference, - Value: "someTokenValue", - } - testutils.MustExec(t, testutils.DB.Save(&tok), "preparing token") - - dat := `{"inactive_reminder": false}` - url := fmt.Sprintf("/account/email-preference?token=%s", "someNonexistentToken") - req := testutils.MakeReq(server.URL, "PATCH", url, dat) - - // Execute - res := testutils.HTTPDo(t, req) - - // Test - assert.StatusCodeEquals(t, res, http.StatusUnauthorized, "") - - var preference database.EmailPreference - testutils.MustExec(t, testutils.DB.Where("user_id = ?", u.ID).First(&preference), "finding preference") - assert.Equal(t, preference.InactiveReminder, true, "email mismatch") - }) - - t.Run("with expired token", func(t *testing.T) { - defer testutils.ClearData(testutils.DB) - - // Setup - server := MustNewServer(t, &app.App{ - - Clock: clock.NewMock(), - }) - defer server.Close() - - u := testutils.SetupUserData() - testutils.SetupEmailPreferenceData(u, true) - - usedAt := time.Now().Add(-11 * time.Minute) - tok := database.Token{ - UserID: u.ID, - Type: database.TokenTypeEmailPreference, - Value: "someTokenValue", - UsedAt: &usedAt, - } - testutils.MustExec(t, testutils.DB.Save(&tok), "preparing token") - - // Execute - dat := `{"inactive_reminder": false}` - url := fmt.Sprintf("/account/email-preference?token=%s", "someTokenValue") - req := testutils.MakeReq(server.URL, "PATCH", url, dat) - res := testutils.HTTPDo(t, req) - - // Test - assert.StatusCodeEquals(t, res, http.StatusUnauthorized, "") - - var preference database.EmailPreference - testutils.MustExec(t, testutils.DB.Where("user_id = ?", u.ID).First(&preference), "finding preference") - assert.Equal(t, preference.InactiveReminder, true, "email mismatch") - }) - - t.Run("with a used but unexpired token", func(t *testing.T) { - - defer testutils.ClearData(testutils.DB) - - // Setup - server := MustNewServer(t, &app.App{ - - Clock: clock.NewMock(), - }) - defer server.Close() - - u := testutils.SetupUserData() - testutils.SetupEmailPreferenceData(u, true) - usedAt := time.Now().Add(-9 * time.Minute) - tok := database.Token{ - UserID: u.ID, - Type: database.TokenTypeEmailPreference, - Value: "someTokenValue", - UsedAt: &usedAt, - } - testutils.MustExec(t, testutils.DB.Save(&tok), "preparing token") - - dat := `{"inactive_reminder": false}` - url := fmt.Sprintf("/account/email-preference?token=%s", "someTokenValue") - req := testutils.MakeReq(server.URL, "PATCH", url, dat) - - // Execute - res := testutils.HTTPDo(t, req) - - // Test - assert.StatusCodeEquals(t, res, http.StatusOK, "") - - var preference database.EmailPreference - testutils.MustExec(t, testutils.DB.Where("user_id = ?", u.ID).First(&preference), "finding preference") - assert.Equal(t, preference.InactiveReminder, false, "InactiveReminder mismatch") - }) - - t.Run("no user and no token", func(t *testing.T) { - - defer testutils.ClearData(testutils.DB) - - // Setup - server := MustNewServer(t, &app.App{ - - Clock: clock.NewMock(), - }) - defer server.Close() - - u := testutils.SetupUserData() - testutils.SetupEmailPreferenceData(u, true) - - // Execute - dat := `{"inactive_reminder": false}` - req := testutils.MakeReq(server.URL, "PATCH", "/account/email-preference", dat) - res := testutils.HTTPDo(t, req) - - // Test - assert.StatusCodeEquals(t, res, http.StatusUnauthorized, "") - - var preference database.EmailPreference - testutils.MustExec(t, testutils.DB.Where("user_id = ?", u.ID).First(&preference), "finding preference") - assert.Equal(t, preference.InactiveReminder, true, "email mismatch") - }) - - t.Run("create a record if not exists", func(t *testing.T) { - - defer testutils.ClearData(testutils.DB) - - // Setup - server := MustNewServer(t, &app.App{ - - Clock: clock.NewMock(), - }) - defer server.Close() - - u := testutils.SetupUserData() - tok := database.Token{ - UserID: u.ID, - Type: database.TokenTypeEmailPreference, - Value: "someTokenValue", - } - testutils.MustExec(t, testutils.DB.Save(&tok), "preparing token") - - // Execute - dat := `{"inactive_reminder": false}` - url := fmt.Sprintf("/account/email-preference?token=%s", "someTokenValue") - req := testutils.MakeReq(server.URL, "PATCH", url, dat) - res := testutils.HTTPDo(t, req) - - // Test - assert.StatusCodeEquals(t, res, http.StatusOK, "") - - var preferenceCount int - testutils.MustExec(t, testutils.DB.Model(database.EmailPreference{}).Count(&preferenceCount), "counting preference") - assert.Equal(t, preferenceCount, 1, "preference count mismatch") - - var preference database.EmailPreference - testutils.MustExec(t, testutils.DB.Where("user_id = ?", u.ID).First(&preference), "finding preference") - assert.Equal(t, preference.InactiveReminder, false, "email mismatch") - }) -} - -func TestGetEmailPreference(t *testing.T) { - defer testutils.ClearData(testutils.DB) - // Setup - server := MustNewServer(t, &app.App{ - - Clock: clock.NewMock(), - }) - defer server.Close() - - u := testutils.SetupUserData() - pref := testutils.SetupEmailPreferenceData(u, true) - - // Execute - req := testutils.MakeReq(server.URL, "GET", "/account/email-preference", "") - res := testutils.HTTPAuthDo(t, req, u) - - // Test - assert.StatusCodeEquals(t, res, http.StatusOK, "") - - var got presenters.EmailPreference - if err := json.NewDecoder(res.Body).Decode(&got); err != nil { - t.Fatal(errors.Wrap(err, "decoding payload")) - } - - expected := presenters.EmailPreference{ - InactiveReminder: pref.InactiveReminder, - ProductUpdate: pref.ProductUpdate, - CreatedAt: presenters.FormatTS(pref.CreatedAt), - UpdatedAt: presenters.FormatTS(pref.UpdatedAt), - } - assert.DeepEqual(t, got, expected, "payload mismatch") -} diff --git a/pkg/server/api/v3_auth.go b/pkg/server/api/v3_auth.go deleted file mode 100644 index 580f82e1..00000000 --- a/pkg/server/api/v3_auth.go +++ /dev/null @@ -1,226 +0,0 @@ -/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd - * - * This file is part of Dnote. - * - * Dnote is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Dnote is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Dnote. If not, see . - */ - -package api - -import ( - "encoding/json" - "net/http" - "time" - - "github.com/dnote/dnote/pkg/server/database" - "github.com/dnote/dnote/pkg/server/handlers" - "github.com/dnote/dnote/pkg/server/log" - "github.com/jinzhu/gorm" - "github.com/pkg/errors" - "golang.org/x/crypto/bcrypt" -) - -// ErrLoginFailure is an error for failed login -var ErrLoginFailure = errors.New("Wrong email and password combination") - -// SessionResponse is a response containing a session information -type SessionResponse struct { - Key string `json:"key"` - ExpiresAt int64 `json:"expires_at"` -} - -func setSessionCookie(w http.ResponseWriter, key string, expires time.Time) { - cookie := http.Cookie{ - Name: "id", - Value: key, - Expires: expires, - Path: "/", - HttpOnly: true, - } - http.SetCookie(w, &cookie) -} - -func touchLastLoginAt(db *gorm.DB, user database.User) error { - t := time.Now() - if err := db.Model(&user).Update(database.User{LastLoginAt: &t}).Error; err != nil { - return errors.Wrap(err, "updating last_login_at") - } - - return nil -} - -type signinPayload struct { - Email string `json:"email"` - Password string `json:"password"` -} - -func (a *API) signin(w http.ResponseWriter, r *http.Request) { - var params signinPayload - err := json.NewDecoder(r.Body).Decode(¶ms) - if err != nil { - handlers.DoError(w, "decoding payload", err, http.StatusInternalServerError) - return - } - if params.Email == "" || params.Password == "" { - http.Error(w, ErrLoginFailure.Error(), http.StatusUnauthorized) - return - } - - var account database.Account - conn := a.App.DB.Where("email = ?", params.Email).First(&account) - if conn.RecordNotFound() { - http.Error(w, ErrLoginFailure.Error(), http.StatusUnauthorized) - return - } else if conn.Error != nil { - handlers.DoError(w, "getting user", err, http.StatusInternalServerError) - return - } - - password := []byte(params.Password) - err = bcrypt.CompareHashAndPassword([]byte(account.Password.String), password) - if err != nil { - http.Error(w, ErrLoginFailure.Error(), http.StatusUnauthorized) - return - } - - var user database.User - err = a.App.DB.Where("id = ?", account.UserID).First(&user).Error - if err != nil { - handlers.DoError(w, "finding user", err, http.StatusInternalServerError) - return - } - - err = a.App.TouchLastLoginAt(user, a.App.DB) - if err != nil { - http.Error(w, errors.Wrap(err, "touching login timestamp").Error(), http.StatusInternalServerError) - return - } - - a.respondWithSession(a.App.DB, w, account.UserID, http.StatusOK) -} - -func (a *API) signoutOptions(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Access-Control-Allow-Methods", "POST") - w.Header().Set("Access-Control-Allow-Headers", "Authorization, Version") -} - -func (a *API) signout(w http.ResponseWriter, r *http.Request) { - key, err := handlers.GetCredential(r) - if err != nil { - handlers.DoError(w, "getting credential", nil, http.StatusInternalServerError) - return - } - - if key == "" { - w.WriteHeader(http.StatusNoContent) - return - } - - err = a.App.DeleteSession(key) - if err != nil { - handlers.DoError(w, "deleting session", nil, http.StatusInternalServerError) - return - } - - handlers.UnsetSessionCookie(w) - w.WriteHeader(http.StatusNoContent) -} - -type registerPayload struct { - Email string `json:"email"` - Password string `json:"password"` -} - -func validateRegisterPayload(p registerPayload) error { - if p.Email == "" { - return errors.New("email is required") - } - if len(p.Password) < 8 { - return errors.New("Password should be longer than 8 characters") - } - - return nil -} - -func parseRegisterPaylaod(r *http.Request) (registerPayload, error) { - var ret registerPayload - if err := json.NewDecoder(r.Body).Decode(&ret); err != nil { - return ret, errors.Wrap(err, "decoding json") - } - - return ret, nil -} - -func (a *API) register(w http.ResponseWriter, r *http.Request) { - if a.App.Config.DisableRegistration { - handlers.RespondForbidden(w) - return - } - - params, err := parseRegisterPaylaod(r) - if err != nil { - http.Error(w, "invalid payload", http.StatusBadRequest) - return - } - if err := validateRegisterPayload(params); err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - var count int - if err := a.App.DB.Model(database.Account{}).Where("email = ?", params.Email).Count(&count).Error; err != nil { - handlers.DoError(w, "checking duplicate user", err, http.StatusInternalServerError) - return - } - if count > 0 { - http.Error(w, "Duplicate email", http.StatusBadRequest) - return - } - - user, err := a.App.CreateUser(params.Email, params.Password) - if err != nil { - handlers.DoError(w, "creating user", err, http.StatusInternalServerError) - return - } - - a.respondWithSession(a.App.DB, w, user.ID, http.StatusCreated) - - if err := a.App.SendWelcomeEmail(params.Email); err != nil { - log.ErrorWrap(err, "sending welcome email") - } -} - -// respondWithSession makes a HTTP response with the session from the user with the given userID. -// It sets the HTTP-Only cookie for browser clients and also sends a JSON response for non-browser clients. -func (a *API) respondWithSession(db *gorm.DB, w http.ResponseWriter, userID int, statusCode int) { - session, err := a.App.CreateSession(userID) - if err != nil { - handlers.DoError(w, "creating session", nil, http.StatusBadRequest) - return - } - - setSessionCookie(w, session.Key, session.ExpiresAt) - - response := SessionResponse{ - Key: session.Key, - ExpiresAt: session.ExpiresAt.Unix(), - } - - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(statusCode) - if err := json.NewEncoder(w).Encode(response); err != nil { - handlers.DoError(w, "encoding response", err, http.StatusInternalServerError) - return - } -} diff --git a/pkg/server/api/v3_auth_test.go b/pkg/server/api/v3_auth_test.go deleted file mode 100644 index 2aa4761e..00000000 --- a/pkg/server/api/v3_auth_test.go +++ /dev/null @@ -1,482 +0,0 @@ -/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd - * - * This file is part of Dnote. - * - * Dnote is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Dnote is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Dnote. If not, see . - */ - -package api - -import ( - "encoding/json" - "fmt" - "net/http" - "testing" - "time" - - "github.com/dnote/dnote/pkg/assert" - "github.com/dnote/dnote/pkg/clock" - "github.com/dnote/dnote/pkg/server/app" - "github.com/dnote/dnote/pkg/server/config" - "github.com/dnote/dnote/pkg/server/database" - "github.com/dnote/dnote/pkg/server/testutils" - "github.com/pkg/errors" - "golang.org/x/crypto/bcrypt" -) - -func assertSessionResp(t *testing.T, res *http.Response) { - // after register, should sign in user - var got SessionResponse - if err := json.NewDecoder(res.Body).Decode(&got); err != nil { - t.Fatal(errors.Wrap(err, "decoding payload")) - } - - var sessionCount int - var session database.Session - testutils.MustExec(t, testutils.DB.Model(&database.Session{}).Count(&sessionCount), "counting session") - testutils.MustExec(t, testutils.DB.First(&session), "getting session") - - assert.Equal(t, sessionCount, 1, "sessionCount mismatch") - assert.Equal(t, got.Key, session.Key, "session Key mismatch") - assert.Equal(t, got.ExpiresAt, session.ExpiresAt.Unix(), "session ExpiresAt mismatch") - - c := testutils.GetCookieByName(res.Cookies(), "id") - assert.Equal(t, c.Value, session.Key, "session key mismatch") - assert.Equal(t, c.Path, "/", "session path mismatch") - assert.Equal(t, c.HttpOnly, true, "session HTTPOnly mismatch") - assert.Equal(t, c.Expires.Unix(), session.ExpiresAt.Unix(), "session Expires mismatch") -} - -func TestRegister(t *testing.T) { - testCases := []struct { - email string - password string - onPremise bool - expectedPro bool - }{ - { - email: "alice@example.com", - password: "pass1234", - onPremise: false, - expectedPro: false, - }, - { - email: "bob@example.com", - password: "Y9EwmjH@Jq6y5a64MSACUoM4w7SAhzvY", - onPremise: false, - expectedPro: false, - }, - { - email: "chuck@example.com", - password: "e*H@kJi^vXbWEcD9T5^Am!Y@7#Po2@PC", - onPremise: false, - expectedPro: false, - }, - // on premise - { - email: "dan@example.com", - password: "e*H@kJi^vXbWEcD9T5^Am!Y@7#Po2@PC", - onPremise: true, - expectedPro: true, - }, - } - - for _, tc := range testCases { - t.Run(fmt.Sprintf("register %s %s", tc.email, tc.password), func(t *testing.T) { - defer testutils.ClearData(testutils.DB) - - c := config.Load() - c.SetOnPremise(tc.onPremise) - - // Setup - emailBackend := testutils.MockEmailbackendImplementation{} - server := MustNewServer(t, &app.App{ - Clock: clock.NewMock(), - EmailBackend: &emailBackend, - Config: c, - }) - defer server.Close() - - dat := fmt.Sprintf(`{"email": "%s", "password": "%s"}`, tc.email, tc.password) - req := testutils.MakeReq(server.URL, "POST", "/v3/register", dat) - - // Execute - res := testutils.HTTPDo(t, req) - - // Test - assert.StatusCodeEquals(t, res, http.StatusCreated, "") - - var account database.Account - testutils.MustExec(t, testutils.DB.Where("email = ?", tc.email).First(&account), "finding account") - assert.Equal(t, account.Email.String, tc.email, "Email mismatch") - assert.NotEqual(t, account.UserID, 0, "UserID mismatch") - passwordErr := bcrypt.CompareHashAndPassword([]byte(account.Password.String), []byte(tc.password)) - assert.Equal(t, passwordErr, nil, "Password mismatch") - - var user database.User - testutils.MustExec(t, testutils.DB.Where("id = ?", account.UserID).First(&user), "finding user") - assert.Equal(t, user.Cloud, tc.expectedPro, "Cloud mismatch") - assert.Equal(t, user.MaxUSN, 0, "MaxUSN mismatch") - - // welcome email - assert.Equalf(t, len(emailBackend.Emails), 1, "email queue count mismatch") - assert.DeepEqual(t, emailBackend.Emails[0].To, []string{tc.email}, "email to mismatch") - - // after register, should sign in user - assertSessionResp(t, res) - }) - } -} - -func TestRegisterMissingParams(t *testing.T) { - t.Run("missing email", func(t *testing.T) { - defer testutils.ClearData(testutils.DB) - - // Setup - server := MustNewServer(t, &app.App{ - Clock: clock.NewMock(), - }) - defer server.Close() - - dat := fmt.Sprintf(`{"password": %s}`, "SLMZFM5RmSjA5vfXnG5lPOnrpZSbtmV76cnAcrlr2yU") - req := testutils.MakeReq(server.URL, "POST", "/v3/register", dat) - - // Execute - res := testutils.HTTPDo(t, req) - - // Test - assert.StatusCodeEquals(t, res, http.StatusBadRequest, "Status mismatch") - - var accountCount, userCount int - testutils.MustExec(t, testutils.DB.Model(&database.Account{}).Count(&accountCount), "counting account") - testutils.MustExec(t, testutils.DB.Model(&database.User{}).Count(&userCount), "counting user") - - assert.Equal(t, accountCount, 0, "accountCount mismatch") - assert.Equal(t, userCount, 0, "userCount mismatch") - }) - - t.Run("missing password", func(t *testing.T) { - - defer testutils.ClearData(testutils.DB) - - // Setup - server := MustNewServer(t, &app.App{ - Clock: clock.NewMock(), - }) - defer server.Close() - - dat := fmt.Sprintf(`{"email": "%s"}`, "alice@example.com") - req := testutils.MakeReq(server.URL, "POST", "/v3/register", dat) - - // Execute - res := testutils.HTTPDo(t, req) - - // Test - assert.StatusCodeEquals(t, res, http.StatusBadRequest, "Status mismatch") - - var accountCount, userCount int - testutils.MustExec(t, testutils.DB.Model(&database.Account{}).Count(&accountCount), "counting account") - testutils.MustExec(t, testutils.DB.Model(&database.User{}).Count(&userCount), "counting user") - - assert.Equal(t, accountCount, 0, "accountCount mismatch") - assert.Equal(t, userCount, 0, "userCount mismatch") - }) -} - -func TestRegisterDuplicateEmail(t *testing.T) { - defer testutils.ClearData(testutils.DB) - - // Setup - server := MustNewServer(t, &app.App{ - Clock: clock.NewMock(), - }) - defer server.Close() - - u := testutils.SetupUserData() - testutils.SetupAccountData(u, "alice@example.com", "somepassword") - - dat := `{"email": "alice@example.com", "password": "foobarbaz"}` - req := testutils.MakeReq(server.URL, "POST", "/v3/register", dat) - - // Execute - res := testutils.HTTPDo(t, req) - - // Test - assert.StatusCodeEquals(t, res, http.StatusBadRequest, "status code mismatch") - - var accountCount, userCount, verificationTokenCount int - testutils.MustExec(t, testutils.DB.Model(&database.Account{}).Count(&accountCount), "counting account") - testutils.MustExec(t, testutils.DB.Model(&database.User{}).Count(&userCount), "counting user") - testutils.MustExec(t, testutils.DB.Model(&database.Token{}).Count(&verificationTokenCount), "counting verification token") - - var user database.User - testutils.MustExec(t, testutils.DB.Where("id = ?", u.ID).First(&user), "finding user") - - assert.Equal(t, accountCount, 1, "account count mismatch") - assert.Equal(t, userCount, 1, "user count mismatch") - assert.Equal(t, verificationTokenCount, 0, "verification_token should not have been created") - assert.Equal(t, user.LastLoginAt, (*time.Time)(nil), "LastLoginAt mismatch") -} - -func TestRegisterDisabled(t *testing.T) { - defer testutils.ClearData(testutils.DB) - - c := config.Load() - c.DisableRegistration = true - - // Setup - server := MustNewServer(t, &app.App{ - Clock: clock.NewMock(), - Config: c, - }) - defer server.Close() - - dat := `{"email": "alice@example.com", "password": "foobarbaz"}` - req := testutils.MakeReq(server.URL, "POST", "/v3/register", dat) - - // Execute - res := testutils.HTTPDo(t, req) - - // Test - assert.StatusCodeEquals(t, res, http.StatusForbidden, "status code mismatch") - - var accountCount, userCount int - testutils.MustExec(t, testutils.DB.Model(&database.Account{}).Count(&accountCount), "counting account") - testutils.MustExec(t, testutils.DB.Model(&database.User{}).Count(&userCount), "counting user") - - assert.Equal(t, accountCount, 0, "account count mismatch") - assert.Equal(t, userCount, 0, "user count mismatch") -} - -func TestSignIn(t *testing.T) { - t.Run("success", func(t *testing.T) { - defer testutils.ClearData(testutils.DB) - - // Setup - server := MustNewServer(t, &app.App{ - - Clock: clock.NewMock(), - }) - defer server.Close() - - u := testutils.SetupUserData() - testutils.SetupAccountData(u, "alice@example.com", "pass1234") - - dat := `{"email": "alice@example.com", "password": "pass1234"}` - req := testutils.MakeReq(server.URL, "POST", "/v3/signin", dat) - - // Execute - res := testutils.HTTPDo(t, req) - - // Test - assert.StatusCodeEquals(t, res, http.StatusOK, "") - - var user database.User - testutils.MustExec(t, testutils.DB.Model(&database.User{}).First(&user), "finding user") - assert.NotEqual(t, user.LastLoginAt, nil, "LastLoginAt mismatch") - - // after register, should sign in user - assertSessionResp(t, res) - }) - - t.Run("wrong password", func(t *testing.T) { - defer testutils.ClearData(testutils.DB) - - // Setup - server := MustNewServer(t, &app.App{ - - Clock: clock.NewMock(), - }) - defer server.Close() - - u := testutils.SetupUserData() - testutils.SetupAccountData(u, "alice@example.com", "pass1234") - - dat := `{"email": "alice@example.com", "password": "wrongpassword1234"}` - req := testutils.MakeReq(server.URL, "POST", "/v3/signin", dat) - - // Execute - res := testutils.HTTPDo(t, req) - - // Test - assert.StatusCodeEquals(t, res, http.StatusUnauthorized, "") - - var user database.User - testutils.MustExec(t, testutils.DB.Model(&database.User{}).First(&user), "finding user") - assert.Equal(t, user.LastLoginAt, (*time.Time)(nil), "LastLoginAt mismatch") - - var sessionCount int - testutils.MustExec(t, testutils.DB.Model(&database.Session{}).Count(&sessionCount), "counting session") - assert.Equal(t, sessionCount, 0, "sessionCount mismatch") - }) - - t.Run("wrong email", func(t *testing.T) { - - defer testutils.ClearData(testutils.DB) - - // Setup - server := MustNewServer(t, &app.App{ - - Clock: clock.NewMock(), - }) - defer server.Close() - - u := testutils.SetupUserData() - testutils.SetupAccountData(u, "alice@example.com", "pass1234") - - dat := `{"email": "bob@example.com", "password": "pass1234"}` - req := testutils.MakeReq(server.URL, "POST", "/v3/signin", dat) - - // Execute - res := testutils.HTTPDo(t, req) - - // Test - assert.StatusCodeEquals(t, res, http.StatusUnauthorized, "") - - var user database.User - testutils.MustExec(t, testutils.DB.Model(&database.User{}).First(&user), "finding user") - assert.DeepEqual(t, user.LastLoginAt, (*time.Time)(nil), "LastLoginAt mismatch") - - var sessionCount int - testutils.MustExec(t, testutils.DB.Model(&database.Session{}).Count(&sessionCount), "counting session") - assert.Equal(t, sessionCount, 0, "sessionCount mismatch") - }) - - t.Run("nonexistent email", func(t *testing.T) { - - defer testutils.ClearData(testutils.DB) - - // Setup - server := MustNewServer(t, &app.App{ - - Clock: clock.NewMock(), - }) - defer server.Close() - - dat := `{"email": "nonexistent@example.com", "password": "pass1234"}` - req := testutils.MakeReq(server.URL, "POST", "/v3/signin", dat) - - // Execute - res := testutils.HTTPDo(t, req) - - // Test - assert.StatusCodeEquals(t, res, http.StatusUnauthorized, "") - - var sessionCount int - testutils.MustExec(t, testutils.DB.Model(&database.Session{}).Count(&sessionCount), "counting session") - assert.Equal(t, sessionCount, 0, "sessionCount mismatch") - }) -} - -func TestSignout(t *testing.T) { - t.Run("authenticated", func(t *testing.T) { - - defer testutils.ClearData(testutils.DB) - - aliceUser := testutils.SetupUserData() - testutils.SetupAccountData(aliceUser, "alice@example.com", "pass1234") - anotherUser := testutils.SetupUserData() - - session1 := database.Session{ - Key: "A9xgggqzTHETy++GDi1NpDNe0iyqosPm9bitdeNGkJU=", - UserID: aliceUser.ID, - ExpiresAt: time.Now().Add(time.Hour * 24), - } - testutils.MustExec(t, testutils.DB.Save(&session1), "preparing session1") - session2 := database.Session{ - Key: "MDCpbvCRg7W2sH6S870wqLqZDZTObYeVd0PzOekfo/A=", - UserID: anotherUser.ID, - ExpiresAt: time.Now().Add(time.Hour * 24), - } - testutils.MustExec(t, testutils.DB.Save(&session2), "preparing session2") - - // Setup - server := MustNewServer(t, &app.App{ - - Clock: clock.NewMock(), - }) - defer server.Close() - - // Execute - req := testutils.MakeReq(server.URL, "POST", "/v3/signout", "") - req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", "A9xgggqzTHETy++GDi1NpDNe0iyqosPm9bitdeNGkJU=")) - res := testutils.HTTPDo(t, req) - - // Test - assert.StatusCodeEquals(t, res, http.StatusNoContent, "Status mismatch") - - var sessionCount int - var s2 database.Session - testutils.MustExec(t, testutils.DB.Model(&database.Session{}).Count(&sessionCount), "counting session") - testutils.MustExec(t, testutils.DB.Where("key = ?", "MDCpbvCRg7W2sH6S870wqLqZDZTObYeVd0PzOekfo/A=").First(&s2), "getting s2") - - assert.Equal(t, sessionCount, 1, "sessionCount mismatch") - - c := testutils.GetCookieByName(res.Cookies(), "id") - assert.Equal(t, c.Value, "", "session key mismatch") - assert.Equal(t, c.Path, "/", "session path mismatch") - assert.Equal(t, c.HttpOnly, true, "session HTTPOnly mismatch") - if c.Expires.After(time.Now()) { - t.Error("session cookie is not expired") - } - }) - - t.Run("unauthenticated", func(t *testing.T) { - - defer testutils.ClearData(testutils.DB) - - aliceUser := testutils.SetupUserData() - testutils.SetupAccountData(aliceUser, "alice@example.com", "pass1234") - anotherUser := testutils.SetupUserData() - - session1 := database.Session{ - Key: "A9xgggqzTHETy++GDi1NpDNe0iyqosPm9bitdeNGkJU=", - UserID: aliceUser.ID, - ExpiresAt: time.Now().Add(time.Hour * 24), - } - testutils.MustExec(t, testutils.DB.Save(&session1), "preparing session1") - session2 := database.Session{ - Key: "MDCpbvCRg7W2sH6S870wqLqZDZTObYeVd0PzOekfo/A=", - UserID: anotherUser.ID, - ExpiresAt: time.Now().Add(time.Hour * 24), - } - testutils.MustExec(t, testutils.DB.Save(&session2), "preparing session2") - - // Setup - server := MustNewServer(t, &app.App{ - - Clock: clock.NewMock(), - }) - defer server.Close() - - // Execute - req := testutils.MakeReq(server.URL, "POST", "/v3/signout", "") - res := testutils.HTTPDo(t, req) - - // Test - assert.StatusCodeEquals(t, res, http.StatusNoContent, "Status mismatch") - - var sessionCount int - var postSession1, postSession2 database.Session - testutils.MustExec(t, testutils.DB.Model(&database.Session{}).Count(&sessionCount), "counting session") - testutils.MustExec(t, testutils.DB.Where("key = ?", "A9xgggqzTHETy++GDi1NpDNe0iyqosPm9bitdeNGkJU=").First(&postSession1), "getting postSession1") - testutils.MustExec(t, testutils.DB.Where("key = ?", "MDCpbvCRg7W2sH6S870wqLqZDZTObYeVd0PzOekfo/A=").First(&postSession2), "getting postSession2") - - // two existing sessions should remain - assert.Equal(t, sessionCount, 2, "sessionCount mismatch") - - c := testutils.GetCookieByName(res.Cookies(), "id") - assert.Equal(t, c, (*http.Cookie)(nil), "id cookie should have not been set") - }) -} diff --git a/pkg/server/api/v3_books.go b/pkg/server/api/v3_books.go deleted file mode 100644 index 34985bb5..00000000 --- a/pkg/server/api/v3_books.go +++ /dev/null @@ -1,267 +0,0 @@ -/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd - * - * This file is part of Dnote. - * - * Dnote is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Dnote is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Dnote. If not, see . - */ - -package api - -import ( - "encoding/json" - "fmt" - "net/http" - "net/url" - - "github.com/dnote/dnote/pkg/server/database" - "github.com/dnote/dnote/pkg/server/handlers" - "github.com/dnote/dnote/pkg/server/helpers" - "github.com/dnote/dnote/pkg/server/presenters" - "github.com/gorilla/mux" - "github.com/jinzhu/gorm" - "github.com/pkg/errors" -) - -type createBookPayload struct { - Name string `json:"name"` -} - -// CreateBookResp is the response from create book api -type CreateBookResp struct { - Book presenters.Book `json:"book"` -} - -func validateCreateBookPayload(p createBookPayload) error { - if p.Name == "" { - return errors.New("name is required") - } - - return nil -} - -// CreateBook creates a new book -func (a *API) CreateBook(w http.ResponseWriter, r *http.Request) { - user, ok := r.Context().Value(helpers.KeyUser).(database.User) - if !ok { - return - } - - var params createBookPayload - err := json.NewDecoder(r.Body).Decode(¶ms) - if err != nil { - handlers.DoError(w, "decoding payload", err, http.StatusInternalServerError) - return - } - - err = validateCreateBookPayload(params) - if err != nil { - handlers.DoError(w, "validating payload", err, http.StatusBadRequest) - return - } - - var bookCount int - err = a.App.DB.Model(database.Book{}). - Where("user_id = ? AND label = ?", user.ID, params.Name). - Count(&bookCount).Error - if err != nil { - handlers.DoError(w, "checking duplicate", err, http.StatusInternalServerError) - return - } - if bookCount > 0 { - http.Error(w, "duplicate book exists", http.StatusConflict) - return - } - - book, err := a.App.CreateBook(user, params.Name) - if err != nil { - handlers.DoError(w, "inserting book", err, http.StatusInternalServerError) - } - resp := CreateBookResp{ - Book: presenters.PresentBook(book), - } - handlers.RespondJSON(w, http.StatusCreated, resp) -} - -// BooksOptions is a handler for OPTIONS endpoint for notes -func (a *API) BooksOptions(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Access-Control-Allow-Methods", "GET, POST") - w.Header().Set("Access-Control-Allow-Headers", "Authorization, Version") -} - -func respondWithBooks(db *gorm.DB, userID int, query url.Values, w http.ResponseWriter) { - var books []database.Book - conn := db.Where("user_id = ? AND NOT deleted", userID).Order("label ASC") - name := query.Get("name") - encryptedStr := query.Get("encrypted") - - if name != "" { - part := fmt.Sprintf("%%%s%%", name) - conn = conn.Where("LOWER(label) LIKE ?", part) - } - if encryptedStr != "" { - var encrypted bool - if encryptedStr == "true" { - encrypted = true - } else { - encrypted = false - } - - conn = conn.Where("encrypted = ?", encrypted) - } - - if err := conn.Find(&books).Error; err != nil { - handlers.DoError(w, "finding books", err, http.StatusInternalServerError) - return - } - - presentedBooks := presenters.PresentBooks(books) - handlers.RespondJSON(w, http.StatusOK, presentedBooks) -} - -// GetBooks returns books for the user -func (a *API) GetBooks(w http.ResponseWriter, r *http.Request) { - user, ok := r.Context().Value(helpers.KeyUser).(database.User) - if !ok { - return - } - - query := r.URL.Query() - - respondWithBooks(a.App.DB, user.ID, query, w) -} - -// GetBook returns a book for the user -func (a *API) GetBook(w http.ResponseWriter, r *http.Request) { - user, ok := r.Context().Value(helpers.KeyUser).(database.User) - if !ok { - return - } - - vars := mux.Vars(r) - bookUUID := vars["bookUUID"] - - var book database.Book - conn := a.App.DB.Where("uuid = ? AND user_id = ?", bookUUID, user.ID).First(&book) - - if conn.RecordNotFound() { - w.WriteHeader(http.StatusNotFound) - return - } - if err := conn.Error; err != nil { - handlers.DoError(w, "finding book", err, http.StatusInternalServerError) - return - } - - p := presenters.PresentBook(book) - handlers.RespondJSON(w, http.StatusOK, p) -} - -type updateBookPayload struct { - Name *string `json:"name"` -} - -// UpdateBookResp is the response from create book api -type UpdateBookResp struct { - Book presenters.Book `json:"book"` -} - -// UpdateBook updates a book -func (a *API) UpdateBook(w http.ResponseWriter, r *http.Request) { - user, ok := r.Context().Value(helpers.KeyUser).(database.User) - if !ok { - return - } - - vars := mux.Vars(r) - uuid := vars["bookUUID"] - - tx := a.App.DB.Begin() - - var book database.Book - if err := tx.Where("user_id = ? AND uuid = ?", user.ID, uuid).First(&book).Error; err != nil { - handlers.DoError(w, "finding book", err, http.StatusInternalServerError) - return - } - - var params updateBookPayload - err := json.NewDecoder(r.Body).Decode(¶ms) - if err != nil { - handlers.DoError(w, "decoding payload", err, http.StatusInternalServerError) - return - } - - book, err = a.App.UpdateBook(tx, user, book, params.Name) - if err != nil { - tx.Rollback() - handlers.DoError(w, "updating a book", err, http.StatusInternalServerError) - } - - tx.Commit() - - resp := UpdateBookResp{ - Book: presenters.PresentBook(book), - } - handlers.RespondJSON(w, http.StatusOK, resp) -} - -// DeleteBookResp is the response from create book api -type DeleteBookResp struct { - Status int `json:"status"` - Book presenters.Book `json:"book"` -} - -// DeleteBook removes a book -func (a *API) DeleteBook(w http.ResponseWriter, r *http.Request) { - user, ok := r.Context().Value(helpers.KeyUser).(database.User) - if !ok { - return - } - - vars := mux.Vars(r) - uuid := vars["bookUUID"] - - tx := a.App.DB.Begin() - - var book database.Book - if err := tx.Where("user_id = ? AND uuid = ?", user.ID, uuid).First(&book).Error; err != nil { - handlers.DoError(w, "finding book", err, http.StatusInternalServerError) - return - } - - var notes []database.Note - if err := tx.Where("book_uuid = ? AND NOT deleted", uuid).Order("usn ASC").Find(¬es).Error; err != nil { - handlers.DoError(w, "finding notes", err, http.StatusInternalServerError) - return - } - - for _, note := range notes { - if _, err := a.App.DeleteNote(tx, user, note); err != nil { - handlers.DoError(w, "deleting a note", err, http.StatusInternalServerError) - return - } - } - b, err := a.App.DeleteBook(tx, user, book) - if err != nil { - handlers.DoError(w, "deleting book", err, http.StatusInternalServerError) - return - } - - tx.Commit() - - resp := DeleteBookResp{ - Status: http.StatusOK, - Book: presenters.PresentBook(b), - } - handlers.RespondJSON(w, http.StatusOK, resp) -} diff --git a/pkg/server/api/v3_notes.go b/pkg/server/api/v3_notes.go deleted file mode 100644 index c38ced5a..00000000 --- a/pkg/server/api/v3_notes.go +++ /dev/null @@ -1,220 +0,0 @@ -/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd - * - * This file is part of Dnote. - * - * Dnote is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Dnote is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Dnote. If not, see . - */ - -package api - -import ( - "encoding/json" - "fmt" - "net/http" - - "github.com/dnote/dnote/pkg/server/app" - "github.com/dnote/dnote/pkg/server/database" - "github.com/dnote/dnote/pkg/server/handlers" - "github.com/dnote/dnote/pkg/server/helpers" - "github.com/dnote/dnote/pkg/server/presenters" - "github.com/gorilla/mux" - "github.com/pkg/errors" -) - -type updateNotePayload struct { - BookUUID *string `json:"book_uuid"` - Content *string `json:"content"` - Public *bool `json:"public"` -} - -type updateNoteResp struct { - Status int `json:"status"` - Result presenters.Note `json:"result"` -} - -func validateUpdateNotePayload(p updateNotePayload) bool { - return p.BookUUID != nil || p.Content != nil || p.Public != nil -} - -// UpdateNote updates note -func (a *API) UpdateNote(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - noteUUID := vars["noteUUID"] - - user, ok := r.Context().Value(helpers.KeyUser).(database.User) - if !ok { - handlers.DoError(w, "No authenticated user found", nil, http.StatusInternalServerError) - return - } - - var params updateNotePayload - err := json.NewDecoder(r.Body).Decode(¶ms) - if err != nil { - handlers.DoError(w, "decoding params", err, http.StatusInternalServerError) - return - } - - if ok := validateUpdateNotePayload(params); !ok { - handlers.DoError(w, "Invalid payload", nil, http.StatusBadRequest) - return - } - - var note database.Note - if err := a.App.DB.Where("uuid = ? AND user_id = ?", noteUUID, user.ID).First(¬e).Error; err != nil { - handlers.DoError(w, "finding note", err, http.StatusInternalServerError) - return - } - - tx := a.App.DB.Begin() - - note, err = a.App.UpdateNote(tx, user, note, &app.UpdateNoteParams{ - BookUUID: params.BookUUID, - Content: params.Content, - Public: params.Public, - }) - if err != nil { - tx.Rollback() - handlers.DoError(w, "updating note", err, http.StatusInternalServerError) - return - } - - var book database.Book - if err := tx.Where("uuid = ? AND user_id = ?", note.BookUUID, user.ID).First(&book).Error; err != nil { - tx.Rollback() - handlers.DoError(w, fmt.Sprintf("finding book %s to preload", note.BookUUID), err, http.StatusInternalServerError) - return - } - - tx.Commit() - - // preload associations - note.User = user - note.Book = book - - resp := updateNoteResp{ - Status: http.StatusOK, - Result: presenters.PresentNote(note), - } - handlers.RespondJSON(w, http.StatusOK, resp) -} - -type deleteNoteResp struct { - Status int `json:"status"` - Result presenters.Note `json:"result"` -} - -// DeleteNote removes note -func (a *API) DeleteNote(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - noteUUID := vars["noteUUID"] - - user, ok := r.Context().Value(helpers.KeyUser).(database.User) - if !ok { - handlers.DoError(w, "No authenticated user found", nil, http.StatusInternalServerError) - return - } - - var note database.Note - if err := a.App.DB.Where("uuid = ? AND user_id = ?", noteUUID, user.ID).Preload("Book").First(¬e).Error; err != nil { - handlers.DoError(w, "finding note", err, http.StatusInternalServerError) - return - } - - tx := a.App.DB.Begin() - - n, err := a.App.DeleteNote(tx, user, note) - if err != nil { - tx.Rollback() - handlers.DoError(w, "deleting note", err, http.StatusInternalServerError) - return - } - - tx.Commit() - - resp := deleteNoteResp{ - Status: http.StatusNoContent, - Result: presenters.PresentNote(n), - } - handlers.RespondJSON(w, http.StatusOK, resp) -} - -type createNotePayload struct { - BookUUID string `json:"book_uuid"` - Content string `json:"content"` - AddedOn *int64 `json:"added_on"` - EditedOn *int64 `json:"edited_on"` -} - -func validateCreateNotePayload(p createNotePayload) error { - if p.BookUUID == "" { - return errors.New("bookUUID is required") - } - - return nil -} - -// CreateNoteResp is a response for creating a note -type CreateNoteResp struct { - Result presenters.Note `json:"result"` -} - -// CreateNote creates a note -func (a *API) CreateNote(w http.ResponseWriter, r *http.Request) { - user, ok := r.Context().Value(helpers.KeyUser).(database.User) - if !ok { - handlers.DoError(w, "No authenticated user found", nil, http.StatusInternalServerError) - return - } - - var params createNotePayload - err := json.NewDecoder(r.Body).Decode(¶ms) - if err != nil { - handlers.DoError(w, "decoding payload", err, http.StatusInternalServerError) - return - } - - err = validateCreateNotePayload(params) - if err != nil { - handlers.DoError(w, "validating payload", err, http.StatusBadRequest) - return - } - - var book database.Book - if err := a.App.DB.Where("uuid = ? AND user_id = ?", params.BookUUID, user.ID).First(&book).Error; err != nil { - handlers.DoError(w, "finding book", err, http.StatusInternalServerError) - return - } - - client := getClientType(r) - note, err := a.App.CreateNote(user, params.BookUUID, params.Content, params.AddedOn, params.EditedOn, false, client) - if err != nil { - handlers.DoError(w, "creating note", err, http.StatusInternalServerError) - return - } - - // preload associations - note.User = user - note.Book = book - - resp := CreateNoteResp{ - Result: presenters.PresentNote(note), - } - handlers.RespondJSON(w, http.StatusCreated, resp) -} - -// NotesOptions is a handler for OPTIONS endpoint for notes -func (a *API) NotesOptions(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Access-Control-Allow-Methods", "POST") - w.Header().Set("Access-Control-Allow-Headers", "Authorization, Version") -} diff --git a/pkg/server/api/v3_notes_test.go b/pkg/server/api/v3_notes_test.go deleted file mode 100644 index df30beed..00000000 --- a/pkg/server/api/v3_notes_test.go +++ /dev/null @@ -1,394 +0,0 @@ -/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd - * - * This file is part of Dnote. - * - * Dnote is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Dnote is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Dnote. If not, see . - */ - -package api - -import ( - "fmt" - "net/http" - "testing" - - "github.com/dnote/dnote/pkg/assert" - "github.com/dnote/dnote/pkg/clock" - "github.com/dnote/dnote/pkg/server/app" - "github.com/dnote/dnote/pkg/server/database" - "github.com/dnote/dnote/pkg/server/testutils" -) - -func TestCreateNote(t *testing.T) { - - defer testutils.ClearData(testutils.DB) - - // Setup - server := MustNewServer(t, &app.App{ - - Clock: clock.NewMock(), - }) - defer server.Close() - - user := testutils.SetupUserData() - testutils.MustExec(t, testutils.DB.Model(&user).Update("max_usn", 101), "preparing user max_usn") - - b1 := database.Book{ - UserID: user.ID, - Label: "js", - USN: 58, - } - testutils.MustExec(t, testutils.DB.Save(&b1), "preparing b1") - - // Execute - dat := fmt.Sprintf(`{"book_uuid": "%s", "content": "note content"}`, b1.UUID) - req := testutils.MakeReq(server.URL, "POST", "/v3/notes", dat) - res := testutils.HTTPAuthDo(t, req, user) - - // Test - assert.StatusCodeEquals(t, res, http.StatusCreated, "") - - var noteRecord database.Note - var bookRecord database.Book - var userRecord database.User - var bookCount, noteCount int - testutils.MustExec(t, testutils.DB.Model(&database.Book{}).Count(&bookCount), "counting books") - testutils.MustExec(t, testutils.DB.Model(&database.Note{}).Count(¬eCount), "counting notes") - testutils.MustExec(t, testutils.DB.First(¬eRecord), "finding note") - testutils.MustExec(t, testutils.DB.Where("id = ?", b1.ID).First(&bookRecord), "finding book") - testutils.MustExec(t, testutils.DB.Where("id = ?", user.ID).First(&userRecord), "finding user record") - - assert.Equalf(t, bookCount, 1, "book count mismatch") - assert.Equalf(t, noteCount, 1, "note count mismatch") - - assert.Equal(t, bookRecord.Label, b1.Label, "book name mismatch") - assert.Equal(t, bookRecord.UUID, b1.UUID, "book uuid mismatch") - assert.Equal(t, bookRecord.UserID, b1.UserID, "book user_id mismatch") - assert.Equal(t, bookRecord.USN, 58, "book usn mismatch") - - assert.NotEqual(t, noteRecord.UUID, "", "note uuid should have been generated") - assert.Equal(t, noteRecord.BookUUID, b1.UUID, "note book_uuid mismatch") - assert.Equal(t, noteRecord.Body, "note content", "note content mismatch") - assert.Equal(t, noteRecord.USN, 102, "note usn mismatch") -} - -func TestUpdateNote(t *testing.T) { - updatedBody := "some updated content" - - b1UUID := "37868a8e-a844-4265-9a4f-0be598084733" - b2UUID := "8f3bd424-6aa5-4ed5-910d-e5b38ab09f8c" - - testCases := []struct { - payload string - noteUUID string - noteBookUUID string - noteBody string - notePublic bool - noteDeleted bool - expectedNoteBody string - expectedNoteBookName string - expectedNoteBookUUID string - expectedNotePublic bool - }{ - { - payload: fmt.Sprintf(`{ - "content": "%s" - }`, updatedBody), - noteUUID: "ab50aa32-b232-40d8-b10f-10a7f9134053", - noteBookUUID: b1UUID, - notePublic: false, - noteBody: "original content", - noteDeleted: false, - expectedNoteBookUUID: b1UUID, - expectedNoteBody: "some updated content", - expectedNoteBookName: "css", - expectedNotePublic: false, - }, - { - payload: fmt.Sprintf(`{ - "book_uuid": "%s" - }`, b1UUID), - noteUUID: "ab50aa32-b232-40d8-b10f-10a7f9134053", - noteBookUUID: b1UUID, - notePublic: false, - noteBody: "original content", - noteDeleted: false, - expectedNoteBookUUID: b1UUID, - expectedNoteBody: "original content", - expectedNoteBookName: "css", - expectedNotePublic: false, - }, - { - payload: fmt.Sprintf(`{ - "book_uuid": "%s" - }`, b2UUID), - noteUUID: "ab50aa32-b232-40d8-b10f-10a7f9134053", - noteBookUUID: b1UUID, - notePublic: false, - noteBody: "original content", - noteDeleted: false, - expectedNoteBookUUID: b2UUID, - expectedNoteBody: "original content", - expectedNoteBookName: "js", - expectedNotePublic: false, - }, - { - payload: fmt.Sprintf(`{ - "book_uuid": "%s", - "content": "%s" - }`, b2UUID, updatedBody), - noteUUID: "ab50aa32-b232-40d8-b10f-10a7f9134053", - noteBookUUID: b1UUID, - notePublic: false, - noteBody: "original content", - noteDeleted: false, - expectedNoteBookUUID: b2UUID, - expectedNoteBody: "some updated content", - expectedNoteBookName: "js", - expectedNotePublic: false, - }, - { - payload: fmt.Sprintf(`{ - "book_uuid": "%s", - "content": "%s" - }`, b1UUID, updatedBody), - noteUUID: "ab50aa32-b232-40d8-b10f-10a7f9134053", - noteBookUUID: b1UUID, - notePublic: false, - noteBody: "", - noteDeleted: true, - expectedNoteBookUUID: b1UUID, - expectedNoteBody: updatedBody, - expectedNoteBookName: "js", - expectedNotePublic: false, - }, - { - payload: fmt.Sprintf(`{ - "public": %t - }`, true), - noteUUID: "ab50aa32-b232-40d8-b10f-10a7f9134053", - noteBookUUID: b1UUID, - notePublic: false, - noteBody: "original content", - noteDeleted: false, - expectedNoteBookUUID: b1UUID, - expectedNoteBody: "original content", - expectedNoteBookName: "css", - expectedNotePublic: true, - }, - { - payload: fmt.Sprintf(`{ - "public": %t - }`, false), - noteUUID: "ab50aa32-b232-40d8-b10f-10a7f9134053", - noteBookUUID: b1UUID, - notePublic: true, - noteBody: "original content", - noteDeleted: false, - expectedNoteBookUUID: b1UUID, - expectedNoteBody: "original content", - expectedNoteBookName: "css", - expectedNotePublic: false, - }, - { - payload: fmt.Sprintf(`{ - "content": "%s", - "public": %t - }`, updatedBody, false), - noteUUID: "ab50aa32-b232-40d8-b10f-10a7f9134053", - noteBookUUID: b1UUID, - notePublic: true, - noteBody: "original content", - noteDeleted: false, - expectedNoteBookUUID: b1UUID, - expectedNoteBody: updatedBody, - expectedNoteBookName: "css", - expectedNotePublic: false, - }, - { - payload: fmt.Sprintf(`{ - "book_uuid": "%s", - "content": "%s", - "public": %t - }`, b2UUID, updatedBody, true), - noteUUID: "ab50aa32-b232-40d8-b10f-10a7f9134053", - noteBookUUID: b1UUID, - notePublic: false, - noteBody: "original content", - noteDeleted: false, - expectedNoteBookUUID: b2UUID, - expectedNoteBody: updatedBody, - expectedNoteBookName: "js", - expectedNotePublic: true, - }, - } - - for idx, tc := range testCases { - t.Run(fmt.Sprintf("test case %d", idx), func(t *testing.T) { - - defer testutils.ClearData(testutils.DB) - - // Setup - server := MustNewServer(t, &app.App{ - - Clock: clock.NewMock(), - }) - defer server.Close() - - user := testutils.SetupUserData() - testutils.MustExec(t, testutils.DB.Model(&user).Update("max_usn", 101), "preparing user max_usn") - - b1 := database.Book{ - UUID: b1UUID, - UserID: user.ID, - Label: "css", - } - testutils.MustExec(t, testutils.DB.Save(&b1), "preparing b1") - b2 := database.Book{ - UUID: b2UUID, - UserID: user.ID, - Label: "js", - } - testutils.MustExec(t, testutils.DB.Save(&b2), "preparing b2") - - note := database.Note{ - UserID: user.ID, - UUID: tc.noteUUID, - BookUUID: tc.noteBookUUID, - Body: tc.noteBody, - Deleted: tc.noteDeleted, - Public: tc.notePublic, - } - testutils.MustExec(t, testutils.DB.Save(¬e), "preparing note") - - // Execute - endpoint := fmt.Sprintf("/v3/notes/%s", note.UUID) - req := testutils.MakeReq(server.URL, "PATCH", endpoint, tc.payload) - res := testutils.HTTPAuthDo(t, req, user) - - // Test - assert.StatusCodeEquals(t, res, http.StatusOK, "status code mismatch for test case") - - var bookRecord database.Book - var noteRecord database.Note - var userRecord database.User - var noteCount, bookCount int - testutils.MustExec(t, testutils.DB.Model(&database.Book{}).Count(&bookCount), "counting books") - testutils.MustExec(t, testutils.DB.Model(&database.Note{}).Count(¬eCount), "counting notes") - testutils.MustExec(t, testutils.DB.Where("uuid = ?", note.UUID).First(¬eRecord), "finding note") - testutils.MustExec(t, testutils.DB.Where("id = ?", b1.ID).First(&bookRecord), "finding book") - testutils.MustExec(t, testutils.DB.Where("id = ?", user.ID).First(&userRecord), "finding user record") - - assert.Equalf(t, bookCount, 2, "book count mismatch") - assert.Equalf(t, noteCount, 1, "note count mismatch") - - assert.Equal(t, noteRecord.UUID, tc.noteUUID, "note uuid mismatch for test case") - assert.Equal(t, noteRecord.Body, tc.expectedNoteBody, "note content mismatch for test case") - assert.Equal(t, noteRecord.BookUUID, tc.expectedNoteBookUUID, "note book_uuid mismatch for test case") - assert.Equal(t, noteRecord.Public, tc.expectedNotePublic, "note public mismatch for test case") - assert.Equal(t, noteRecord.USN, 102, "note usn mismatch for test case") - - assert.Equal(t, userRecord.MaxUSN, 102, "user max_usn mismatch for test case") - }) - } -} - -func TestDeleteNote(t *testing.T) { - b1UUID := "37868a8e-a844-4265-9a4f-0be598084733" - - testCases := []struct { - content string - deleted bool - originalUSN int - expectedUSN int - expectedMaxUSN int - }{ - { - content: "n1 content", - deleted: false, - originalUSN: 12, - expectedUSN: 982, - expectedMaxUSN: 982, - }, - { - content: "", - deleted: true, - originalUSN: 12, - expectedUSN: 982, - expectedMaxUSN: 982, - }, - } - - for _, tc := range testCases { - t.Run(fmt.Sprintf("originally deleted %t", tc.deleted), func(t *testing.T) { - - defer testutils.ClearData(testutils.DB) - - // Setup - server := MustNewServer(t, &app.App{ - - Clock: clock.NewMock(), - }) - defer server.Close() - - user := testutils.SetupUserData() - testutils.MustExec(t, testutils.DB.Model(&user).Update("max_usn", 981), "preparing user max_usn") - - b1 := database.Book{ - UUID: b1UUID, - UserID: user.ID, - Label: "js", - } - testutils.MustExec(t, testutils.DB.Save(&b1), "preparing b1") - note := database.Note{ - UserID: user.ID, - BookUUID: b1.UUID, - Body: tc.content, - Deleted: tc.deleted, - USN: tc.originalUSN, - } - testutils.MustExec(t, testutils.DB.Save(¬e), "preparing note") - - // Execute - endpoint := fmt.Sprintf("/v3/notes/%s", note.UUID) - req := testutils.MakeReq(server.URL, "DELETE", endpoint, "") - res := testutils.HTTPAuthDo(t, req, user) - - // Test - assert.StatusCodeEquals(t, res, http.StatusOK, "") - - var bookRecord database.Book - var noteRecord database.Note - var userRecord database.User - var bookCount, noteCount int - testutils.MustExec(t, testutils.DB.Model(&database.Book{}).Count(&bookCount), "counting books") - testutils.MustExec(t, testutils.DB.Model(&database.Note{}).Count(¬eCount), "counting notes") - testutils.MustExec(t, testutils.DB.Where("uuid = ?", note.UUID).First(¬eRecord), "finding note") - testutils.MustExec(t, testutils.DB.Where("id = ?", b1.ID).First(&bookRecord), "finding book") - testutils.MustExec(t, testutils.DB.Where("id = ?", user.ID).First(&userRecord), "finding user record") - - assert.Equalf(t, bookCount, 1, "book count mismatch") - assert.Equalf(t, noteCount, 1, "note count mismatch") - - assert.Equal(t, noteRecord.UUID, note.UUID, "note uuid mismatch for test case") - assert.Equal(t, noteRecord.Body, "", "note content mismatch for test case") - assert.Equal(t, noteRecord.Deleted, true, "note deleted mismatch for test case") - assert.Equal(t, noteRecord.BookUUID, note.BookUUID, "note book_uuid mismatch for test case") - assert.Equal(t, noteRecord.UserID, note.UserID, "note user_id mismatch for test case") - assert.Equal(t, noteRecord.USN, tc.expectedUSN, "note usn mismatch for test case") - - assert.Equal(t, userRecord.MaxUSN, tc.expectedMaxUSN, "user max_usn mismatch for test case") - }) - } -} diff --git a/pkg/server/app/app.go b/pkg/server/app/app.go index c530d4fa..06e50010 100644 --- a/pkg/server/app/app.go +++ b/pkg/server/app/app.go @@ -46,6 +46,7 @@ type App struct { EmailTemplates mailer.Templates EmailBackend mailer.Backend Config config.Config + Files map[string][]byte } // Validate validates the app configuration diff --git a/pkg/server/app/email.go b/pkg/server/app/email.go index 64485340..5a377902 100644 --- a/pkg/server/app/email.go +++ b/pkg/server/app/email.go @@ -116,6 +116,10 @@ func (a *App) SendWelcomeEmail(email string) error { // SendPasswordResetEmail sends password reset email func (a *App) SendPasswordResetEmail(email, tokenValue string) error { + if email == "" { + return ErrEmailRequired + } + body, err := a.EmailTemplates.Execute(mailer.EmailTypeResetPassword, mailer.EmailKindText, mailer.EmailResetPasswordTmplData{ AccountEmail: email, Token: tokenValue, @@ -131,6 +135,10 @@ func (a *App) SendPasswordResetEmail(email, tokenValue string) error { } if err := a.EmailBackend.Queue("Reset your password", from, []string{email}, mailer.EmailKindText, body); err != nil { + if errors.Cause(err) == mailer.ErrSMTPNotConfigured { + return ErrInvalidSMTPConfig + } + return errors.Wrapf(err, "queueing email for %s", email) } diff --git a/pkg/server/app/errors.go b/pkg/server/app/errors.go new file mode 100644 index 00000000..d96b9fb2 --- /dev/null +++ b/pkg/server/app/errors.go @@ -0,0 +1,67 @@ +package app + +type appError string + +func (e appError) Error() string { + return string(e) +} + +func (e appError) Public() string { + return string(e) +} + +var ( + // ErrNotFound an error that indicates that the given resource is not found + ErrNotFound appError = "not found" + // ErrLoginInvalid is an error for invalid login + ErrLoginInvalid appError = "Wrong email and password combination" + + // ErrDuplicateEmail is an error for duplicate email + ErrDuplicateEmail appError = "duplicate email" + // ErrEmailRequired is an error for missing email + ErrEmailRequired appError = "Please enter an email" + // ErrPasswordRequired is an error for missing email + ErrPasswordRequired appError = "Please enter a password" + // ErrPasswordTooShort is an error for short password + ErrPasswordTooShort appError = "password should be longer than 8 characters" + // ErrPasswordConfirmationMismatch is an error for password ans password confirmation not matching + ErrPasswordConfirmationMismatch appError = "password confirmation does not match password" + + // ErrLoginRequired is an error for not authenticated + ErrLoginRequired appError = "login required" + + // ErrBookUUIDRequired is an error for note missing book uuid + ErrBookUUIDRequired appError = "book uuid required" + // ErrBookNameRequired is an error for note missing book name + ErrBookNameRequired appError = "book name required" + // ErrDuplicateBook is an error for duplicate book + ErrDuplicateBook appError = "duplicate book exists" + + // ErrEmptyUpdate is an error for empty update params + ErrEmptyUpdate appError = "update is empty" + + // ErrInvalidUUID is an error for invalid uuid + ErrInvalidUUID appError = "invalid uuid" + + // ErrInvalidSMTPConfig is an error for invalid SMTP configuration + ErrInvalidSMTPConfig appError = "SMTP is not configured" + + // ErrInvalidToken is an error for invalid token + ErrInvalidToken appError = "invalid token" + // ErrMissingToken is an error for missing token + ErrMissingToken appError = "missing token" + // ErrExpiredToken is an error for missing token + ErrExpiredToken appError = "This token has expired." + + // ErrPasswordResetTokenExpired is an error for expired password reset token + ErrPasswordResetTokenExpired appError = "this link has been expired. Please request a new password reset link." + // ErrInvalidPasswordChangeInput is an error for changing password + ErrInvalidPasswordChangeInput appError = "Both current and new passwords are required to change the password." + + ErrInvalidPassword appError = "Invalid currnet password." + // ErrEmailTooLong is an error for email length exceeding the limit + ErrEmailTooLong appError = "Email is too long." + + // ErrEmailAlreadyVerified is an error for trying to verify email that is already verified + ErrEmailAlreadyVerified appError = "Email is already verified." +) diff --git a/pkg/server/app/notes.go b/pkg/server/app/notes.go index 054a4989..83a24b85 100644 --- a/pkg/server/app/notes.go +++ b/pkg/server/app/notes.go @@ -19,6 +19,9 @@ package app import ( + "strings" + "time" + "github.com/dnote/dnote/pkg/server/database" "github.com/dnote/dnote/pkg/server/helpers" "github.com/jinzhu/gorm" @@ -174,3 +177,144 @@ func (a *App) GetUserNoteByUUID(userID int, uuid string) (*database.Note, error) return &ret, nil } + +// GetNotesParams is params for finding notes +type GetNotesParams struct { + Year int + Month int + Page int + Books []string + Search string + Encrypted bool + PerPage int +} + +type ftsParams struct { + HighlightAll bool +} + +func getHeadlineOptions(params *ftsParams) string { + headlineOptions := []string{ + "StartSel=", + "StopSel=", + "ShortWord=0", + } + + if params != nil && params.HighlightAll { + headlineOptions = append(headlineOptions, "HighlightAll=true") + } else { + headlineOptions = append(headlineOptions, "MaxFragments=3, MaxWords=50, MinWords=10") + } + + return strings.Join(headlineOptions, ",") +} + +func selectFTSFields(conn *gorm.DB, search string, params *ftsParams) *gorm.DB { + headlineOpts := getHeadlineOptions(params) + + return conn.Select(` +notes.id, +notes.uuid, +notes.created_at, +notes.updated_at, +notes.book_uuid, +notes.user_id, +notes.added_on, +notes.edited_on, +notes.usn, +notes.deleted, +notes.encrypted, +ts_headline('english_nostop', notes.body, plainto_tsquery('english_nostop', ?), ?) AS body + `, search, headlineOpts) +} + +func getNotesBaseQuery(db *gorm.DB, userID int, q GetNotesParams) *gorm.DB { + conn := db.Where( + "notes.user_id = ? AND notes.deleted = ? AND notes.encrypted = ?", + userID, false, q.Encrypted, + ) + + if q.Search != "" { + conn = selectFTSFields(conn, q.Search, nil) + conn = conn.Where("tsv @@ plainto_tsquery('english_nostop', ?)", q.Search) + } + + if len(q.Books) > 0 { + conn = conn.Joins("INNER JOIN books ON books.uuid = notes.book_uuid"). + Where("books.label in (?)", q.Books) + } + + if q.Year != 0 || q.Month != 0 { + dateLowerbound, dateUpperbound := getDateBounds(q.Year, q.Month) + conn = conn.Where("notes.added_on >= ? AND notes.added_on < ?", dateLowerbound, dateUpperbound) + } + + return conn +} + +func getDateBounds(year, month int) (int64, int64) { + var yearUpperbound, monthUpperbound int + + if month == 12 { + monthUpperbound = 1 + yearUpperbound = year + 1 + } else { + monthUpperbound = month + 1 + yearUpperbound = year + } + + lower := time.Date(year, time.Month(month), 1, 0, 0, 0, 0, time.UTC).UnixNano() + upper := time.Date(yearUpperbound, time.Month(monthUpperbound), 1, 0, 0, 0, 0, time.UTC).UnixNano() + + return lower, upper +} + +func orderGetNotes(conn *gorm.DB) *gorm.DB { + return conn.Order("notes.updated_at DESC, notes.id DESC") +} + +func paginate(conn *gorm.DB, page, perPage int) *gorm.DB { + // Paginate + if page > 0 { + offset := perPage * (page - 1) + conn = conn.Offset(offset) + } + + conn = conn.Limit(perPage) + + return conn +} + +// GetNotesResult is the result of getting notes +type GetNotesResult struct { + Notes []database.Note + Total int +} + +// GetNotes returns a list of matching notes +func (a *App) GetNotes(userID int, params GetNotesParams) (GetNotesResult, error) { + conn := getNotesBaseQuery(a.DB, userID, params) + + var total int + if err := conn.Model(database.Note{}).Count(&total).Error; err != nil { + return GetNotesResult{}, errors.Wrap(err, "counting total") + } + + notes := []database.Note{} + if total != 0 { + conn = orderGetNotes(conn) + conn = database.PreloadNote(conn) + conn = paginate(conn, params.Page, params.PerPage) + + if err := conn.Find(¬es).Error; err != nil { + return GetNotesResult{}, errors.Wrap(err, "finding notes") + } + } + + res := GetNotesResult{ + Notes: notes, + Total: total, + } + + return res, nil +} diff --git a/pkg/server/app/testutils.go b/pkg/server/app/testutils.go index 6645e430..41091625 100644 --- a/pkg/server/app/testutils.go +++ b/pkg/server/app/testutils.go @@ -19,6 +19,7 @@ package app import ( + "fmt" "os" "github.com/dnote/dnote/pkg/clock" @@ -60,6 +61,12 @@ func NewTest(appParams *App) App { if appParams != nil && appParams.Config.DisableRegistration { a.Config.DisableRegistration = appParams.Config.DisableRegistration } + if appParams != nil && appParams.Config.PageTemplateDir != "" { + a.Config.PageTemplateDir = appParams.Config.PageTemplateDir + } + + fmt.Printf("%+v\n", appParams) + fmt.Printf("%+v\n", a) return a } diff --git a/pkg/server/app/users.go b/pkg/server/app/users.go index 9261dcb9..4c3a335f 100644 --- a/pkg/server/app/users.go +++ b/pkg/server/app/users.go @@ -20,6 +20,7 @@ package app import ( "github.com/dnote/dnote/pkg/server/database" + "github.com/dnote/dnote/pkg/server/log" "github.com/dnote/dnote/pkg/server/token" "github.com/jinzhu/gorm" "github.com/pkg/errors" @@ -48,9 +49,29 @@ func createEmailPreference(user database.User, tx *gorm.DB) error { } // CreateUser creates a user -func (a *App) CreateUser(email, password string) (database.User, error) { +func (a *App) CreateUser(email, password string, passwordConfirmation string) (database.User, error) { + if email == "" { + return database.User{}, ErrEmailRequired + } + + if len(password) < 8 { + return database.User{}, ErrPasswordTooShort + } + + if password != passwordConfirmation { + return database.User{}, ErrPasswordConfirmationMismatch + } + tx := a.DB.Begin() + var count int + if err := tx.Model(database.Account{}).Where("email = ?", email).Count(&count).Error; err != nil { + return database.User{}, errors.Wrap(err, "counting user") + } + if count > 0 { + return database.User{}, ErrDuplicateEmail + } + hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) if err != nil { tx.Rollback() @@ -99,3 +120,42 @@ func (a *App) CreateUser(email, password string) (database.User, error) { return user, nil } + +// Authenticate authenticates a user +func (a *App) Authenticate(email, password string) (*database.User, error) { + var account database.Account + conn := a.DB.Where("email = ?", email).First(&account) + if conn.RecordNotFound() { + return nil, ErrNotFound + } else if conn.Error != nil { + return nil, conn.Error + } + + err := bcrypt.CompareHashAndPassword([]byte(account.Password.String), []byte(password)) + if err != nil { + return nil, ErrLoginInvalid + } + + var user database.User + err = a.DB.Where("id = ?", account.UserID).First(&user).Error + if err != nil { + return nil, errors.Wrap(err, "finding user") + } + + return &user, nil +} + +// SignIn signs in a user +func (a *App) SignIn(user *database.User) (*database.Session, error) { + err := a.TouchLastLoginAt(*user, a.DB) + if err != nil { + log.ErrorWrap(err, "touching login timestamp") + } + + session, err := a.CreateSession(user.ID) + if err != nil { + return nil, errors.Wrap(err, "creating session") + } + + return &session, nil +} diff --git a/pkg/server/app/users_test.go b/pkg/server/app/users_test.go index b78458f6..37ea4d6d 100644 --- a/pkg/server/app/users_test.go +++ b/pkg/server/app/users_test.go @@ -27,9 +27,10 @@ import ( "github.com/dnote/dnote/pkg/server/database" "github.com/dnote/dnote/pkg/server/testutils" "github.com/pkg/errors" + "golang.org/x/crypto/bcrypt" ) -func TestCreateUser(t *testing.T) { +func TestCreateUser_ProValue(t *testing.T) { testCases := []struct { onPremise bool expectedPro bool @@ -54,7 +55,7 @@ func TestCreateUser(t *testing.T) { a := NewTest(&App{ Config: c, }) - if _, err := a.CreateUser("alice@example.com", "pass1234"); err != nil { + if _, err := a.CreateUser("alice@example.com", "pass1234", "pass1234"); err != nil { t.Fatal(errors.Wrap(err, "executing")) } @@ -68,3 +69,53 @@ func TestCreateUser(t *testing.T) { }) } } + +func TestCreateUser(t *testing.T) { + t.Run("success", func(t *testing.T) { + defer testutils.ClearData(testutils.DB) + + c := config.Load() + a := NewTest(&App{ + Config: c, + }) + if _, err := a.CreateUser("alice@example.com", "pass1234", "pass1234"); err != nil { + t.Fatal(errors.Wrap(err, "executing")) + } + + var userCount int + testutils.MustExec(t, testutils.DB.Model(&database.User{}).Count(&userCount), "counting user") + assert.Equal(t, userCount, 1, "book count mismatch") + + var accountCount int + var accountRecord database.Account + testutils.MustExec(t, testutils.DB.Model(&database.Account{}).Count(&accountCount), "counting account") + testutils.MustExec(t, testutils.DB.First(&accountRecord), "finding account") + + assert.Equal(t, accountCount, 1, "account count mismatch") + assert.Equal(t, accountRecord.Email.String, "alice@example.com", "account email mismatch") + + passwordErr := bcrypt.CompareHashAndPassword([]byte(accountRecord.Password.String), []byte("pass1234")) + assert.Equal(t, passwordErr, nil, "Password mismatch") + }) + + t.Run("duplicate email", func(t *testing.T) { + defer testutils.ClearData(testutils.DB) + + aliceUser := database.User{} + aliceAccount := database.Account{UserID: aliceUser.ID, Email: database.ToNullString("alice@example.com")} + testutils.MustExec(t, testutils.DB.Save(&aliceUser), "preparing a user") + testutils.MustExec(t, testutils.DB.Save(&aliceAccount), "preparing an account") + + a := NewTest(nil) + _, err := a.CreateUser("alice@example.com", "newpassword", "newpassword") + + assert.Equal(t, err, ErrDuplicateEmail, "error mismatch") + + var userCount, accountCount int + testutils.MustExec(t, testutils.DB.Model(&database.User{}).Count(&userCount), "counting user") + testutils.MustExec(t, testutils.DB.Model(&database.Account{}).Count(&accountCount), "counting account") + + assert.Equal(t, userCount, 1, "user count mismatch") + assert.Equal(t, accountCount, 1, "account count mismatch") + }) +} diff --git a/pkg/server/assets/js/build.sh b/pkg/server/assets/js/build.sh new file mode 100755 index 00000000..4c9cc599 --- /dev/null +++ b/pkg/server/assets/js/build.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +# build.sh builds styles +set -ex + +dir=$(dirname "${BASH_SOURCE[0]}") +basePath="$dir/../../.." +serverDir="$dir/../.." +outputDir="$serverDir/static" +inputDir="$dir/src" + +task="cp $inputDir/main.js $outputDir" + + +if [[ "$1" == "true" ]]; then +( + cd "$basePath/watcher" && \ + go run main.go \ + --task="$task" \ + --context="$inputDir" \ + "$inputDir" +) +else + eval "$task" +fi diff --git a/pkg/server/assets/js/src/main.js b/pkg/server/assets/js/src/main.js new file mode 100644 index 00000000..7d32f867 --- /dev/null +++ b/pkg/server/assets/js/src/main.js @@ -0,0 +1,59 @@ +var getNextSibling = function (el, selector) { + var sibling = el.nextElementSibling; + + if (!selector) { + return sibling; + } + + while (sibling) { + if (sibling.matches(selector)) return sibling; + sibling = sibling.nextElementSibling; + } +}; + +var dropdownTriggerEls = document.getElementsByClassName('dropdown-trigger'); + +for (var i = 0; i < dropdownTriggerEls.length; i++) { + var dropdownTriggerEl = dropdownTriggerEls[i]; + + dropdownTriggerEl.addEventListener('click', function (e) { + var el = getNextSibling(e.target, '.dropdown-content'); + + el.classList.toggle('show'); + }); +} + +// Dropdown closer +window.onclick = function (e) { + // Close dropdown on click outside the dropdown content or trigger + function shouldClose(target) { + var dropdownContentEls = document.getElementsByClassName( + 'dropdown-content' + ); + + for (let i = 0; i < dropdownContentEls.length; ++i) { + var el = dropdownContentEls[i]; + if (el.contains(target)) { + return false; + } + } + for (let i = 0; i < dropdownTriggerEls.length; ++i) { + var el = dropdownTriggerEls[i]; + if (el.contains(target)) { + return false; + } + } + + return true; + } + + if (shouldClose(e.target)) { + var dropdowns = document.getElementsByClassName('dropdown-content'); + for (var i = 0; i < dropdowns.length; i++) { + var openDropdown = dropdowns[i]; + if (openDropdown.classList.contains('show')) { + openDropdown.classList.remove('show'); + } + } + } +}; diff --git a/pkg/server/assets/package-lock.json b/pkg/server/assets/package-lock.json new file mode 100644 index 00000000..101aff52 --- /dev/null +++ b/pkg/server/assets/package-lock.json @@ -0,0 +1,157 @@ +{ + "name": "assets", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "immutable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz", + "integrity": "sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "sass": { + "version": "1.50.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.50.1.tgz", + "integrity": "sha512-noTnY41KnlW2A9P8sdwESpDmo+KBNkukI1i8+hOK3footBUcohNHtdOJbckp46XO95nuvcHDDZ+4tmOnpK3hjw==", + "dev": true, + "requires": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + } + }, + "source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + } + } +} diff --git a/pkg/server/assets/package.json b/pkg/server/assets/package.json new file mode 100644 index 00000000..0edcd180 --- /dev/null +++ b/pkg/server/assets/package.json @@ -0,0 +1,12 @@ +{ + "name": "assets", + "version": "1.0.0", + "description": "assets", + "main": "index.js", + "scripts": {}, + "author": "Dnote", + "license": "AGPL-3.0-or-later", + "devDependencies": { + "sass": "^1.50.1" + } +} diff --git a/pkg/server/assets/static/500.html b/pkg/server/assets/static/500.html new file mode 100644 index 00000000..ccf85d20 --- /dev/null +++ b/pkg/server/assets/static/500.html @@ -0,0 +1,10 @@ + + + + + + + + 500 + + diff --git a/pkg/server/assets/static/android-icon-144x144.png b/pkg/server/assets/static/android-icon-144x144.png new file mode 100644 index 0000000000000000000000000000000000000000..09b28d2f31989bb81fb84e6da6127bb371ae1735 GIT binary patch literal 2448 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 literal 0 HcmV?d00001 diff --git a/pkg/server/assets/static/android-icon-36x36.png b/pkg/server/assets/static/android-icon-36x36.png new file mode 100644 index 0000000000000000000000000000000000000000..7c9f770fb5eac33aff1d8b0d226f12a754e7e53a GIT binary patch literal 1406 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k3?#4J%UA`ZSkfJR9T^xl_H+M9WCijWi-X*q z7}lMWc?skwBzpw;GB8xBF)%c=FfjZA3N^f7U???UV0e|lz+g3lfkC`r&aOZkpg#8i zpAc7|0xc~qZEbB`U0od=9ZgM5BO@cA$Y2IQM^f%QpqGLE1p*)gk7*FQ@Z0$R|9^>j zZ;F6HnOzd(7Yw99fLTCDkWIY%*ol+nOkC6d?ci+v`T6~m)NK==eM(^6a6n~I;WbnJ z%TMRM*y(zKu~Smj?E%le78VJC3{e^{r)#K%th&RdZ8bG0Eak-ar*6*m%&XAGRF$*N*1K1r3j^_PD(3_oN~9lBy+p_(#)H+Z~y<_cQjUFVeInj zzW2BPo>?i@<|yEyP`M}e&8~w+`{e5NcRS4Y@vSdl_{gPsg=dxX;kAN4nzAZVrWC%D z?+$3$exV{{_K(b@!Wk+nHcq`Dk)g$T*_bya@2s2Yg=fzW_bIJhvSWkCyCwP$*P1cZ zTFRDB`F7}(*4`httZv<4Vbs1klP{X_^~-JL7xZT2+a$j+O;w4kyY3;?^zw>Wu{oSJ&SHi7cyVU8;r^x=l{gXxf zSnnF$w|$_iB)z(?85o$VC9V-ADTyViR>?)FK#IZ0z|d0Hz*N`BGQ`l(%Gl7#)KJ^N zz{-L1;F+74p6Z*Jo|&AjV5VoT zXQ6AU0JPEsWTl~zLZG3ULPkkRft9{~d3m{BCP+0916Awg7p326dkZv>K>}oANJeRH zl9iQ9esXDUYF>$zRRBU0q~!7%MGl}6NhBq{nYpPYl?AB`U{~oEc`~GB=A;6>ub-P&l9QjVpO#pbnVg?j ztdEEoedA;kGm~UdRKsKgOJf6bGxOwRv&58SGqaR513jP)J)rx1*|vcSPzFy| KKbLh*2~7a-L%|{d literal 0 HcmV?d00001 diff --git a/pkg/server/assets/static/android-icon-48x48.png b/pkg/server/assets/static/android-icon-48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..d93d8cdaa37497a779c2bf1f515a278446540e4c GIT binary patch literal 1454 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sEa{HEjtmSN`?>!lvI6;x#X;^) z4C~IxyaaL-l0AZa85pY67#JE_7#My5g&JNkFq8sKd6mGxU^Rn*LA+qju0R{0KHmVJ z5LciAb#-+O4Gk?VElo{LeSLiq5fL699z8uhRaI49US6OSL>8!k7651|Nyfs}Yin!c z*N9&MrYz9QK=0!>6RRx5N&o-^OG#P^QmoPG07T)7dBgo)*UBS6rB{EOa*X z=eeW_ecjqzMg2W1zA`e(ZMmf{yZMgu-&M8s2l#^$^_X5X*ed{SV@&dPcga)Y4-^M- zI14-?iy0WWg+Z8+Vb&Z8pn?!j7sn8f<8P;ghaWPKaB#L~@3NQ>B`WH=v>~eSec;hX zrE9i@53l|CAKtxG(KdJTuV>8Pw|-xC|75wJfYOheMm0Xhg^QFq%(idYl*zr-`6z$4 z?2Ps+oKx-}{c0%I_-$!ko&e*m_b*d+Q6UB9MXy&|}<e7qGdwghzdO`mD0L!p^6AR0YCO+Z!}8Yu)Cnki(IloVL$>z9|8>t%ve12IswUVc&fowm0? z0~sVhCWd5`<|bKLx#TC8=BDPASXl)Cl@>D?F8{wH2AjWtqwOdBysOh|xDrHZe0vHa9o4NKQ3OHn21{FgG(#PBu$SNj5V}Ni)y`>d*ta U&zEf*sK90LboFyt=akR{0LpyI(*OVf literal 0 HcmV?d00001 diff --git a/pkg/server/assets/static/android-icon-72x72.png b/pkg/server/assets/static/android-icon-72x72.png new file mode 100644 index 0000000000000000000000000000000000000000..aa2e9876e6f3eae66384217780561587e9262b8d GIT binary patch literal 1583 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAifOS+@4BLl<6e(pbstUx|vage(c z!@6@aFM%9|WRD45bDP46hOx7_4S6Fo+k-*%fF5)aMo8 z6XFU~prxgytE;Q2si~u*qoJXpt*x!1qGD`p3={_nLI99W5CByYRD-DiXd_S%qE}sA z9a8~T5lp+_B5(#)jg$*wHUIzr|DCrY>VTn{QxfDC45T4|nO{hdO|MXctjR6FmMZlFeAgPITAnxH$7b(Ln02py%8RK$U%hlLCj(evki%iE}CKiIvQGK zi<1s^DQJCbJN3W5?6QZMwqQbr`y!xqHS5vd4h>1HeM_!UC;)-=lbFSvb7|YJ7 zp?4-K-kx=>+v4jCWye3StgLc1ehbw9&CPo$BPq1)VrF`HsnqNRRTdt{x>Fb0Ua))o zo4sAMc)}LOmb6vQq3i8i%eZ9!GCkR#z`Y5Eewk0W{$KdW?LL1k^_s4@E$7ZHE&jF0 zxy2~b*hTci68(!M@3^v>4;0rnw|(x5Hi>0s$=x(r=tyFjM`4ksW0i7RTflnjO8K8> zXNexF-ss#GcVqSK zfs@meMRsq1Qej9^p+TMuX_+~xK=144=9T2+r|YLBmSraA=N0QCB1Ydh*~H8w+1%XF wB01GC*}&4+z}(C{IoT{RCE3g@CCxw&s6!9vK3}$Npc0?K)78&qol`;+07D1{E&u=k literal 0 HcmV?d00001 diff --git a/pkg/server/assets/static/android-icon-96x96.png b/pkg/server/assets/static/android-icon-96x96.png new file mode 100644 index 0000000000000000000000000000000000000000..6d711b98bfcf06da7ea4884de74888b71b073747 GIT binary patch literal 1777 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD3?#3*wSy#B(j9#r85lP9bN@+X1@ak-gWR1M z)}51i3FIgwdj$D1FjT2AFf_C ztVI~@wmpCI+SuvR60UPn7i4}t{rz`E^|v*$5#RrPRfzg9BPjMyIk(b&uA)5Y9pZDM zfAme7I62vxk(rTC?T&=b!?P?c+FN*7c{w>(ZQi-NjFo+H=&Mg3zs%xTAnD@fuFIt7 zTKDdh`3W|w)5=@b6h*}x-ak;eIlD{#aoi%HBN&ss-CbHmuV+06aySb-B8wRqxP?KO zkzv*x2?hoxYfl%)kc@k8Z@vr{3Y0na@wfCclZ#H;s)34Imn?G0P@U{moE^C7s*$Po z*$l-Z&$Vlw@%JC)=J_5scjswK+4Hsc&hGh9$2UhZtmWHthF5#OF8XZ7u;EvG{|~9z zeO&7-V+Juz*|JnQ|9w@n=o>mg%ta{yo7E#wouRPM#af)ljtm z_m!7&>(YMs@Jp|FF~7Aq#^K4I*0}g7j5mJOI4sdmbNHhqyWuOZp2DT_lP5~`+A|u~ zzJFN7(rRaPMmvxz;k~llf*bY>;p+qEEm~#A-|)NhwWH9Acf|}(>vp>`xZJmOcs-qA z(|qfz_0Bu`8NK8sAIN&#E7|pA?QKJ^dUgg0m0vFwSe6_=+h#k(j?HaXr``DzK>GM6 z_P~StI3HiF<+-zWy8RA&tt;-&nmHfIrm)Y@VOZHdRo{or$5yIv&v7~Cnf~*l^cwC= z{rYC-W#9c3obMRTzdN4hVSn(PdM=CQuXrae-qePUiP22)*Ec@XXIS`{o{Z3Q@=R2)oZ_1{_vmE`$cd#QWk=LsydpQtb4#b5q3Ubn!}Z(iwcO)BOBa+j0MnLgiEBhjN@7W> zRdP`(kYX@0FtpS)Fx54(3^6pcGB&g_HPkjRure^P{%E%WMMG|WN@iLmZVg^*+IWB( z+(0%I=ckpFCl;kLc;+Uir}`$QXC`MWnCY48S?F3S0If6uS!rmb5NK$okWo@nV5P5L zUS6)32~rKjK-GHrMd^3i-U1C|kN}w&l2MwQWM$=&pIn-onpa|F6#!IP%wV|u|89LW zHDEiftTIwF(=$pK3@wfL8H!qgYQ&MufvO45Oex6#DY?8xkprki5=n_~W^QUpWkD(f z*j4%kdFl3>!u-(8@eKj0VlXr{wKO!cG&MAI+R2gxRKkyBPH<*bDuaQO)09PaZ-7!^ zNK&Ceo(yT3IjKPJ>*wZ`mwpY-#FRC%p}>|+|VL9)iBw>(%8V< m%se^SEHNe7%q%6%Ko6)x59mH$wr!v~i^0>?&t;ucLK6V!j!wJ) literal 0 HcmV?d00001 diff --git a/pkg/server/assets/static/apple-icon-114x114.png b/pkg/server/assets/static/apple-icon-114x114.png new file mode 100644 index 0000000000000000000000000000000000000000..e6b7fece234c57eb935fe9a8fda21038a32a116e GIT binary patch literal 2154 zcmeAS@N?(olHy`uVBq!ia0vp^MIg+<3?z3jm-+)KmUKs7M+SzC{oH>NS%G}U;vjb? zhIQv;UIIA^$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBsPB4! zPlzi}ftHq*rlzL0wzh_bhKh=cs;X*eXsDi^o`HdZj*gC+nwq-0y1u@?w6wITsj0ZQ zxS^q;uCA`Jv9X+-98eah3#c7v2#o-;;Xu3a*n+GQ&W1DKdf_4vqtLi$=A+3%0tBuG z7-$eSoB@|Y(}<}Is0A2|Km&n64cCR00W=D$X>i48vj6}8ua;Q60~oclOM?7@fixyy zWMg5}E}A^0J3@fT^USXg9XwuFpV@tj|DJU1;k`RCD?+)K+zn%9KRKcA*iVkR%FIgh ze@o3?DZ#yg#rcu6>EVB~B#jTvIk=29rPiI*L?y9?k*V%)6VJk2!R>ijOT(GAbuo9c zL~>4loWR7)#MJQd4R5_L-={4ani~0g3;)<;-u&`H-O!O=`ol|OpxYReyxm>q6z(~s z4&-nactjR6FmMZlFeAgPIT8#E%n_b0jv*QM-ro8h%@ZiY_M!M(&Xv4(L5fqoB9^*( zbuV+-t=AnjWs#Fho+2mb#3iNs-`i*F?LJ#xrk9<5Zf5&)%lG-#&u{)Mxi{1H^5%4b zU(1^~pFUWlq!;CGdQ?KVdgGFkziquH!P{2eDVw)rQBiT|q`Xzx+irAijn-@q+LpUL zdur_v_*xltD;__wjx_Jf7O*{D*-uDyam8r!>eojd-p!A)xYRgs&|H-wh<9WshtJ%Q!> z@wBy}nt#Qs7VljCJ!w_wqI}Uws?2F?y*PQ==BlV^tksjhw4Lk3fu&)a!18EZI)K6L{F;=-IXvE zG(LH5cAMp+f7|W^>#Q$+aqaDs_zC4aZ@=7^%|2!M{hjs`=JX|tmnQsKsD0^VQ}|&| zzoJQ#=R8qSw>*i#{3CzDko`%Em(`!#zZX*?->nR-i>{C1=j*kvTs7(7%AAykn_u2B zp0j+<*PUfgTz{UHoLXZ#p;x78@`>9AIkMM(e0?)3ROV&%zWa`=hAfJH%GJSZ`6)4Jj+ff3Etad!n?_8$l6+OmIw-4@ z_v4f)(O)AwR=jwuu2#GyE7a>jF>|Tmy8ZT2LjOK}jg~#jvcLb|hu=9aE$sDv*DjyA zvi^R>+P_>+-w5jde!gAc)3qhv9mMATVXWD&^^t{9x(S%^R7+eVN>UO_QmvAUQh^kM zk%6J5u7Rnpk!6UXp_Q?rm8qe&fq|8Qf%Qkb4JaCN^HVa@DsgM@TGPe@)ZhlPp*TOS zq&%@GmBBMNF+J5cF+DRmTft1vT+c$+QUPeC3CK!ABZWXiGlh(jk^(Dz{qpj1y-bj5 zAO@<|%P&g5)Akl znwg$a!eD4=#LrOF3REMGWDZnKcxFmT21v=}HHsWSC6Y)=d^2-XODYRe8Nja6FUU)` z-xTJDW{z(NP!)rrsi~!*k)^4jq0>&5B%l(0By)l@t5O*ZoSddCvU>xR3PX|#4f142 z%gjjydS5>`uOufwT|X_cEHgPjuUH=uG5W^ICT1qd=H`YL$*G3P2A0MK=4R%}$!3Wu g$!2CLX$E>g9eP0b`Lb;TH5M2=UHx3vIVCg!09JGPPXGV_ literal 0 HcmV?d00001 diff --git a/pkg/server/assets/static/apple-icon-120x120.png b/pkg/server/assets/static/apple-icon-120x120.png new file mode 100644 index 0000000000000000000000000000000000000000..871eb9d0ecc63c859f7f3942c21df05027fa7a90 GIT binary patch literal 2181 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 R+U$QN| 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 literal 0 HcmV?d00001 diff --git a/pkg/server/assets/static/apple-icon-60x60.png b/pkg/server/assets/static/apple-icon-60x60.png new file mode 100644 index 0000000000000000000000000000000000000000..9dbf917e1d90d5373987b75dcd65e1a2e0acc8f0 GIT binary patch literal 1566 zcmeAS@N?(olHy`uVBq!ia0vp^HXzKw3=&b&bO2H;>5jgR3=A9lx&I`x0{M)^LGDfr z>(0r%1acITJ%W507^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MS0K<-#`plA z5LciAEiEl=ZEXz=4NXl=H8nL=RaJF$bz@^=pg4U1&|X?t1T@yj$OxCo`uh5~r1bRk za7mFQ{{R1fMNaW1U=SCV1o;I6X$X*a_jVCAJMsO;x1~a+M_(9OTzsv6?BDO>yEg1s zZoW3>l0lmZ3oc?Ra^gBudu(cN&S3x&m^n23R9lHF%l z-~adDm{b$3?OGbz0 zf8vVBO`Y1HdetssO_Z}oZmimjb5~!Oe{JM3cyL#)E zw)L7DFIH{l_K^umE%_c%ut|H-%7mn~;ssy7&IU>ZPZbrr=Hw7oap2F{mFsp*&^0XB z5Z-p$IAdFG*;0>5O&%p0xoz$#Pg>k{w{s;wb_lNn ztWZlj$4t{TOz&fEozYJy?5sI{I=($MwN%&lnz`A=t$&sOEqpuoZu|pVcIz7l`N}@* zKlH}_?}<7S-$(527FK<)<^6@0pHw~9wv*|pj+^GpD&o$^~sQj^}jh~264<6+C zO+Z!}8Yu)Cnki(IloVL$>z9|8>t%ve12IswUVc&fowm0?0~sVhCWd5`<|bKLx#TC8 z=BDPASXl)Cl@>D?F8{wH2AjWtqwOdBysOh|xDr zHZe0vHa9o4NKQ3OHn21{FgG(#PBu$SNj5V}Ni)y`>d*ta&zEf*sDNkiboFyt=akR{ E00!j45bDP46hOx7_4S6Fo+k-*%fF5)aMo8 z6XFU~prxgytE;Q2si~u*qoJXpt*x!1qGD`p3={_nLI99W5CByYRD-DiXd_S%qE}sA z9a8~T5lp+_B5(#)jg$*wHUIzr|DCrY>VTn{QxfDC45T4|nO{hdO|MXctjR6FmMZlFeAgPITAnxH$7b(Ln02py%8RK$U%hlLCj(evki%iE}CKiIvQGK zi<1s^DQJCbJN3W5?6QZMwqQbr`y!xqHS5vd4h>1HeM_!UC;)-=lbFSvb7|YJ7 zp?4-K-kx=>+v4jCWye3StgLc1ehbw9&CPo$BPq1)VrF`HsnqNRRTdt{x>Fb0Ua))o zo4sAMc)}LOmb6vQq3i8i%eZ9!GCkR#z`Y5Eewk0W{$KdW?LL1k^_s4@E$7ZHE&jF0 zxy2~b*hTci68(!M@3^v>4;0rnw|(x5Hi>0s$=x(r=tyFjM`4ksW0i7RTflnjO8K8> zXNexF-ss#GcVqSK zfs@meMRsq1Qej9^p+TMuX_+~xK=144=9T2+r|YLBmSraA=N0QCB1Ydh*~H8w+1%XF wB01GC*}&4+z}(C{IoT{RCE3g@CCxw&s6!9vK3}$Npc0?K)78&qol`;+07D1{E&u=k literal 0 HcmV?d00001 diff --git a/pkg/server/assets/static/apple-icon-76x76.png b/pkg/server/assets/static/apple-icon-76x76.png new file mode 100644 index 0000000000000000000000000000000000000000..5e4af4bc80d6bff26e8a67e907680cc462508130 GIT binary patch literal 1676 zcmeAS@N?(olHy`uVBq!ia0vp^J|N7&3?x5zE|dgPEa{HEjtmSN`?>!lvI6;x#X;^) z4C~IxyaaL-l0AZa85pY67#JE_7#My5g&JNkFq9fFFuY1&V6d9Oz#v{QXIG#NP+xX{ zPlzi}fu^RWmX?-=hK9DbHjoVgKoLp+&=8We18pZsJzO!wBDi%xFTkZR8JMz|BAB}H zh`{Z|qXr`N|NnoUwAKP(aF>?^`2_=MU<|NxGV*7w-ncf8x9`Wd_@1B7{{5cP`D?mB z>H8hq-U*jIPvYT9|MVh`S?Fl2#2LTHh(B!vsE>F{9twjdtiUNhqKw;b0CY8yxmEv`%ibgj6E3q z@P9*Q?8M7~*HeGh_8dL0V<|3_K0SPm!6i|Dr&AlJo||L6e^=U}W3iqEeX06SR8GX3 zBt3bf{L#kZ_@(JZLQSW`c_gm3$|UdLc6^wXA+&hcgk&A-S6VT1 zcF-d{=Su6w*-O{l_1ex8%fkHM>T2tSp6B1%{+^n8QjeGU#GQq^B+M=@O$_>~XZO3> zb4KD8vzK~Jq1&#kzEyiokA>f>OR{=Job9Uj_2LP;XKcE8sjteef4Ge<%z}SUt@`Rizxfq*9$Rqt;C!Qte^+q5Gidmxp7Zq=^Plx?-aRFm)3klw zU(zj)*uD6|H10E3o^mP2-rab#H?pKK*`RKphWGJTIWtnH_g%H)KdmEO_c9`arMvd; zv29y&5*6Q0H@IF^yZ+&P$Cbb4UN$OyxcJY+2b-c|lf|#q{p(_z@%{Jt`;9lHju!TQ z)J(r?zgz6M#nQAx?|?}~wZt`|BqgyV)hf9t6-Y4{85mmX8kp)DS%w%IS{WN!nHp*v z7+4t?SbwzJfTAHcKP5A*61N7gHEldV4Q?PCiu2P-$`gxH89Z|n(^GvD(=(H^70mR^ z^(=HP6@XTnfUGn$QV29OQ^+VODX`MlFE20G%LJ(gVxVfh{G#+bZEt}FGDv_-49O_X zO|r6b$xklLP0cH@vI+nyEoLxW{(rYVni{a3R#q9QnduoN42G6Q{0v2{KsDk>=0Mei zXQq^7fRtQbqsReNB8jBLH#0Z2q_QBD0qiRMg1mJ5O<{g$=JvSVrG(TZfgTe~DWM4f6{|U@ literal 0 HcmV?d00001 diff --git a/pkg/server/assets/static/apple-icon-precomposed.png b/pkg/server/assets/static/apple-icon-precomposed.png new file mode 100644 index 0000000000000000000000000000000000000000..f3410b4e444689670c06fcceec564e901abb66e7 GIT binary patch literal 2303 zcmb`H3s93+7RT?G@CXD!!AD|g2rf?rk^l)3p@|p-q`c!I)>cCD5mJbW5Z)M4*8-iX z(yFZ>AQZdGR7F6`ilR`drWS?T#TW9b;s|0y3W6kjVK1_i$-1+%)0y6xd+#~t{?7m0 z``tU=XVH-%ODtDg0sxkTh4NzI+`iBWcsQcCN+z64#2X_v0?=4WoD-NrAIXRf2?F(9 z_M_0D3PWQf0LXIzK(!A5Eexqf0LY{R@GKDkb{PPxv%Jh_O3AKmYr|FL3Zu%N4Paad0+jh;2Yjbihi)8vSv6 z_Vvq7iKX4q8I@V(Tqy3U%}@G!H}7x*aaDKmJzr;ia(-g(yNE@2m(-MX-$bM}eb@Bd z*%i08PCNhnmzK$?QN(Ejn_*^gD30>b>ly6<^>Z;+oR)C-;FrF~K1@Y6NnO1@&bq#A z-}l6Vl3yQfi9#L|2AVxa$atR+muu=X_JrY+cE7`&AxYsOyrX4ssG9cv3{Z!Y}w#jP-q&-rRBQx=JL zsxG`!l+<4l2d1%8sjO!MJ1B|c;<)b3BiWf)TZEaXfFsHt+|>wk zG4}9jwDYo|zS3B3+Wd{eyuTI08vju8aV|Qtc@M=A^yy*+u_U(`CFAe_@klLiXrKPOUA&UfIh^8LwS_hOqCB=*&DLZU{E7OvtxQ}%#^+T9NZ zmkr%1wN>#syFjUu4~Dh7&2pxX1L}iPJ8o_D6kep;ZIUx{C`+Z_Sc7uqb>vm>$$$Dv z#oy{0kzws;5j9wvy2Up_JxgrD?jx~So&8zt47i;ebHd^8pcFde7kTTuoqkb!7E`E@&AwqA+sdulgr8G9byXP%d9h|E)NR`|^j5 z5Nx#U6ZJ&C^D(RSdmfFRd%AF6PVXbcP!z)C?=~0Y-p-d5RFl*49Fh&kc&2A-xK?J8-TjKUSX zu9N<}eNH1YZ6H5TUce1^SG%g3I3aITf1On;=at&i`H$T-vJ%?Xi}B4?XR7a)?IdQ7 zSFZ1wbjUXuym&%?dG(Cn-i+`u#dsj&@{_@bds?sYL>TE~eM0>nCDx@EnvLqz$pdq@ zLqm1E&FDb?fgcj}$7hKLjx=U8;0~XHH|DGe-lh-<6ewGmj=~9e(;5Drbf%}bKcB&1 z`!Lu{#yUEkO{Z^|c(36<5~MPbBsuqgCu})gWCas8FI32oB`dN8=_m-33dCrHKr9ia zQdwSpUh6&msj#H)LP>@b}SEN~ZWTXW}eoCe^ ziOuH8gqc!wmx9fXhG+(0oSeTzdy9Eflg&;+CE^qXU^2Yhe~FHP%qk3qCks@UQ zu`j^)L4<6CL`bBlB3F)rH@l+AcZoSoe~o-gCxQB8VZk3Q%L2sWI>ihDBG1m^AY;8e1(2~jP*j4A*B2J h(EV6`LLo~a60%sLWcqi~g94}p!h$1t^+Aby{{>+JBEkRw literal 0 HcmV?d00001 diff --git a/pkg/server/assets/static/apple-icon.png b/pkg/server/assets/static/apple-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..f3410b4e444689670c06fcceec564e901abb66e7 GIT binary patch literal 2303 zcmb`H3s93+7RT?G@CXD!!AD|g2rf?rk^l)3p@|p-q`c!I)>cCD5mJbW5Z)M4*8-iX z(yFZ>AQZdGR7F6`ilR`drWS?T#TW9b;s|0y3W6kjVK1_i$-1+%)0y6xd+#~t{?7m0 z``tU=XVH-%ODtDg0sxkTh4NzI+`iBWcsQcCN+z64#2X_v0?=4WoD-NrAIXRf2?F(9 z_M_0D3PWQf0LXIzK(!A5Eexqf0LY{R@GKDkb{PPxv%Jh_O3AKmYr|FL3Zu%N4Paad0+jh;2Yjbihi)8vSv6 z_Vvq7iKX4q8I@V(Tqy3U%}@G!H}7x*aaDKmJzr;ia(-g(yNE@2m(-MX-$bM}eb@Bd z*%i08PCNhnmzK$?QN(Ejn_*^gD30>b>ly6<^>Z;+oR)C-;FrF~K1@Y6NnO1@&bq#A z-}l6Vl3yQfi9#L|2AVxa$atR+muu=X_JrY+cE7`&AxYsOyrX4ssG9cv3{Z!Y}w#jP-q&-rRBQx=JL zsxG`!l+<4l2d1%8sjO!MJ1B|c;<)b3BiWf)TZEaXfFsHt+|>wk zG4}9jwDYo|zS3B3+Wd{eyuTI08vju8aV|Qtc@M=A^yy*+u_U(`CFAe_@klLiXrKPOUA&UfIh^8LwS_hOqCB=*&DLZU{E7OvtxQ}%#^+T9NZ zmkr%1wN>#syFjUu4~Dh7&2pxX1L}iPJ8o_D6kep;ZIUx{C`+Z_Sc7uqb>vm>$$$Dv z#oy{0kzws;5j9wvy2Up_JxgrD?jx~So&8zt47i;ebHd^8pcFde7kTTuoqkb!7E`E@&AwqA+sdulgr8G9byXP%d9h|E)NR`|^j5 z5Nx#U6ZJ&C^D(RSdmfFRd%AF6PVXbcP!z)C?=~0Y-p-d5RFl*49Fh&kc&2A-xK?J8-TjKUSX zu9N<}eNH1YZ6H5TUce1^SG%g3I3aITf1On;=at&i`H$T-vJ%?Xi}B4?XR7a)?IdQ7 zSFZ1wbjUXuym&%?dG(Cn-i+`u#dsj&@{_@bds?sYL>TE~eM0>nCDx@EnvLqz$pdq@ zLqm1E&FDb?fgcj}$7hKLjx=U8;0~XHH|DGe-lh-<6ewGmj=~9e(;5Drbf%}bKcB&1 z`!Lu{#yUEkO{Z^|c(36<5~MPbBsuqgCu})gWCas8FI32oB`dN8=_m-33dCrHKr9ia zQdwSpUh6&msj#H)LP>@b}SEN~ZWTXW}eoCe^ ziOuH8gqc!wmx9fXhG+(0oSeTzdy9Eflg&;+CE^qXU^2Yhe~FHP%qk3qCks@UQ zu`j^)L4<6CL`bBlB3F)rH@l+AcZoSoe~o-gCxQB8VZk3Q%L2sWI>ihDBG1m^AY;8e1(2~jP*j4A*B2J h(EV6`LLo~a60%sLWcqi~g94}p!h$1t^+Aby{{>+JBEkRw literal 0 HcmV?d00001 diff --git a/pkg/server/assets/static/browserconfig.xml b/pkg/server/assets/static/browserconfig.xml new file mode 100644 index 00000000..c5541482 --- /dev/null +++ b/pkg/server/assets/static/browserconfig.xml @@ -0,0 +1,2 @@ + +#ffffff \ No newline at end of file diff --git a/pkg/server/assets/static/favicon-16x16.png b/pkg/server/assets/static/favicon-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..798fbd5c4c35fe8841edeb104b8cf497bf0eb73f GIT binary patch literal 1061 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstUx|vage(c z!@6@aFM%9|WRD45bDP46hOx7_4S6Fo+k-*%fF5lsFLJ z6XFV_wT3MC|Ns9oc8+;KKiHK7`2hpRaoY45UJl2;{QKOyKjr+!tgrq1g1E{iPio%Y zH>HCkp`zTRvr@2_Z|S)+F&DZSc?H-rk61_UsjjWr>pt=1xmTq?lNgh{-CgYMk_|u{ z&H|6fVg?3oVGw3ym^DWNDA?}l;us=vdF$z$Vh0UGS|1kA5(@Ek6!u!Vvu#$=wD0w6 z)Fy?0xcjz#EpNjM!TXv&Q}<>rU9~1^!K;S28yUMUWR*)?$XhR8xbP#R>?#SjWFNK5 z9^UE2OPM1dT#5~QGCy(e$!$^FyuTkj*z%|1m!)0@a8knFCc5 zo|#gT0a9{#jUoq7i6oK|-^|?9lFEWq2C%F23-Z$KH--73nd2J*RK;LuYHDd{WNB(> z=(Lk138;i0$(-QKs#FF8C#NZk?A`#S!jPmwgFG41GILUa-q+8~E6K@E*H23<%S_JC zE7nIujJ|QQiJ3{Vxw)Z5a;jmnfu*s5xtV!#vRPtEvYA;*nt>irhaS*!lvI6;x#X;^) z4C~IxyacIC_6YK2V5m}KU}$JzVE6?TYIwoGP-?)y@G60U!DgvkM%9@&*+S=Mc?r;J?AL)Prg66v2o+_IJ?Jv9zpL*Kdj;VPZh-l_i;SWm4I+lFy#F-Nr2m5($ zNa(Jeq`)PzHaAC7QsV6!*&G3fM8MJ8bw^ zIVu-0iLU&>Q6eD6IXOLGOIMfJAr`}^fJ(N1H!N}-lZ4_?V*yvKe!iM4gq3f)x z6O^X_Lrb;9HKHUXu_VKa*w7#dm`8(NtfY8x0>85mf9wA+B9 zAvZrIGp!Q02Cp@3JU|U@ARCJF(@M${i&7apa}(23eG}6&ld~1f^vv}vbS)KtR+@mU zG&E8OG&EDlC@Cqh($_C9FW1WisRm-8YQ6lT^gC^Dfd(>2fJ_X@D9uf>vU15!F3nBN zE3vW)04gnJFkJqBw?3L0u$@*`8L64+86^ycmPY&xMXf+J;z;H|)r4oJlw^RETwbHd z0aPN1q{KHfH?^d)Ae8~^D*b}Ibo)(VerV?Sh5%JD7@C?|8X8%e8X7w7WJv-l;YTtj zII}91!NAFB$|AcrK&db!sn8%#hP2F_RG|0ubMs1a^3(Ox63a4^^Ye=J5fP(roNQud zl5B2nXpx+1m~3EaY+!C?o}6r!n38N}mXc|ja BiyQy| literal 0 HcmV?d00001 diff --git a/pkg/server/assets/static/favicon-96x96.png b/pkg/server/assets/static/favicon-96x96.png new file mode 100644 index 0000000000000000000000000000000000000000..6d711b98bfcf06da7ea4884de74888b71b073747 GIT binary patch literal 1777 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD3?#3*wSy#B(j9#r85lP9bN@+X1@ak-gWR1M z)}51i3FIgwdj$D1FjT2AFf_C ztVI~@wmpCI+SuvR60UPn7i4}t{rz`E^|v*$5#RrPRfzg9BPjMyIk(b&uA)5Y9pZDM zfAme7I62vxk(rTC?T&=b!?P?c+FN*7c{w>(ZQi-NjFo+H=&Mg3zs%xTAnD@fuFIt7 zTKDdh`3W|w)5=@b6h*}x-ak;eIlD{#aoi%HBN&ss-CbHmuV+06aySb-B8wRqxP?KO zkzv*x2?hoxYfl%)kc@k8Z@vr{3Y0na@wfCclZ#H;s)34Imn?G0P@U{moE^C7s*$Po z*$l-Z&$Vlw@%JC)=J_5scjswK+4Hsc&hGh9$2UhZtmWHthF5#OF8XZ7u;EvG{|~9z zeO&7-V+Juz*|JnQ|9w@n=o>mg%ta{yo7E#wouRPM#af)ljtm z_m!7&>(YMs@Jp|FF~7Aq#^K4I*0}g7j5mJOI4sdmbNHhqyWuOZp2DT_lP5~`+A|u~ zzJFN7(rRaPMmvxz;k~llf*bY>;p+qEEm~#A-|)NhwWH9Acf|}(>vp>`xZJmOcs-qA z(|qfz_0Bu`8NK8sAIN&#E7|pA?QKJ^dUgg0m0vFwSe6_=+h#k(j?HaXr``DzK>GM6 z_P~StI3HiF<+-zWy8RA&tt;-&nmHfIrm)Y@VOZHdRo{or$5yIv&v7~Cnf~*l^cwC= z{rYC-W#9c3obMRTzdN4hVSn(PdM=CQuXrae-qePUiP22)*Ec@XXIS`{o{Z3Q@=R2)oZ_1{_vmE`$cd#QWk=LsydpQtb4#b5q3Ubn!}Z(iwcO)BOBa+j0MnLgiEBhjN@7W> zRdP`(kYX@0FtpS)Fx54(3^6pcGB&g_HPkjRure^P{%E%WMMG|WN@iLmZVg^*+IWB( z+(0%I=ckpFCl;kLc;+Uir}`$QXC`MWnCY48S?F3S0If6uS!rmb5NK$okWo@nV5P5L zUS6)32~rKjK-GHrMd^3i-U1C|kN}w&l2MwQWM$=&pIn-onpa|F6#!IP%wV|u|89LW zHDEiftTIwF(=$pK3@wfL8H!qgYQ&MufvO45Oex6#DY?8xkprki5=n_~W^QUpWkD(f z*j4%kdFl3>!u-(8@eKj0VlXr{wKO!cG&MAI+R2gxRKkyBPH<*bDuaQO)09PaZ-7!^ zNK&Ceo(yT3IjKPJ>*wZ`mwpY-#FRC%p}>|+|VL9)iBw>(%8V< m%se^SEHNe7%q%6%Ko6)x59mH$wr!v~i^0>?&t;ucLK6V!j!wJ) literal 0 HcmV?d00001 diff --git a/pkg/server/assets/static/favicon.ico b/pkg/server/assets/static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c71572aab7836379227c521fcaea2282b2e607ff GIT binary patch literal 1150 zcmeIyze>YU6vy#X6k4oki_p1a$?T*eE^0Fr zTts~YeFk+^zrQB8gpwx3S;{9j_nw^7aR0gY_RosAxLbQlbR75~tgu1jwQE>_? z5)|910s>-1*^;`1YGs*0iAdS{Y!QP(ipT@Pdvg=;oX(ju{d@js&dK@Sy}x_!_q*Ty z)-PADS~}Wfk_iBeUcPM6CjcD$0dtV`0?C?qob@UV+=2?6J$*|S(tdyza`_*7T?L&3EK|~xVJ8>p2LkE_%wT_8+nf0 ztA0GUH_kdU(ln7a_}iN1O}*>Zrs|0P?_cTNOJl+#B08&r3usmBzM`gUX0UPo)uNCC z4orhaqhVTGTd&WV44m90q*&b69oxIBFN7vuRCt#i7@#%Ok&q;=*U84lMz!i%z`#~L z)^NS4y#7<;&9fHW+TZ?K!U?b>zu11@*W(s$4|zgum&(~m44j{f_o}@$ zS8YVwch9T`x zb0SQ2?ZAe*X%MQQ4er%{5H=2yTo^c&;D@>qp<04o&k?Ow+DP^qO9*ok>TF@eRxOd1 z2>hcOBhf65XbgfgM@k4j3E$emJh7H|$|Y)?D4yb5W63IXaa|63exwcZcW^RY&~_r% zYpGNSxd@`m#&KXJVBm%w+Hc)>_n!SVvfiFLzRt(@fb?# z1uK!0z=RNtMW)NR3NC_KV`!cLf7YYkw{N4vjDOB6$}{Pl22DvCqwSyde8rxh?wg@)|0rS20cKO(-Ns3rUdMpq zH<^s}J`=OwttI<#tX|~=_Ndwln-gBpvLcg%o#g|skHz+$zjKJI*u*C#(d$%w>UO3% zqwI80167gSX*M?UeIk;ecr{h^G^sfeHfYmf&nf?)bP1Fm#BRlY6ua=aL})+p*x;QN zT*Yjh7W2b+l}to89a_!3vZ%nHR#7*^du+Haez% zH5!oDDjp?|dK>E1X)xoW-o8CVTRj$R1)!s1bwVh<#KGx#W2<&x;&^?4stU5`idk)PG+Lz%apBS%Y`%~n;q>Z47Svi0q3Fi+0n6JhW4}QA^Q-m+BH6)w4Z#CqwmzEhR$oE_sP3X*K_qAmo-856WI+hmG(pU zGg>9@?70(mn5WC-ghrNpQO{(nwk_l-=nBSX`^=s7y(`I#!=}S#c^8M1Y70+$XJ#>F z$V~WAubzz)Gq?D?A^Ylog@dlVQZmC~IKInXGu;qaQL^mp;Rrh$^&~6k+QmcG!r7aK zd9@wN)u&uR?{2Ca?t1@|b??4XejDy_^W`1Gt`W8NLv&>OZf~9>V7A8eziH3P_Ro85 zCW@)f@R;w+lvXN{OfuE+;F5a!D3P_4eH;E}9F9}VTBV$4q03c~EhUAntFGbd*7{L4`a)i+!9V<0^Iy+LN+fZUH)@{Qk+=7e6uUfuIhKY1#yN5f;IxFX$d zgmbB)P$!m@xUslMqor(Gor$DS%?5b140&HW&dgEN>1Lpk+*vqss0udCLsiG(ZlT{* zjX!U=xPga9OZs_{%^2UWf=yPEo}oQ60a^x)0gR^sOg;%9@xqgcGxCp$RMYg^ctWIR zJND1;QYA2bZOsSR!CoW7y2(JVwPK&R#lM=E8J6l0@OyZCPNnT1eg7-iUyA=6+`q=- zukrY^#rOX-KLQ*ge|!G?`F62bEQf$NM%%ZhK9Qm*`IEMzr>}Z0ZJOya1?VR!(|xU< z+|A;2jp&*C3(xj(?xrH^GiN`?J*DZFo!ZW?%e7@W%K;RI^Q_p~6*ohCFvq4$l_uy4 z5xE-gbR{{t&DOsJ%24^N>IzXBg z#qQFHPE6-gL@ku;AZ8o-15NS?P;^PzBly}FRY{YUDCSGq3Z3|X&eIuNQgTn!u9mWM zu!Fk-rgkiXKhVA_0BSrap$7q8Dk&R^dvtM#)(u^1Y$^E$?Cv$6t;9fk?$bp`_{CvO zgbp;Mn!iTEYAG3qHo4AB1A4LyZAgd52D343O_i39S*_ zh^G~THkEWTCegJ<%0AVJkLYAy6uqVF0!*gqyhg@D<2Wa#8;3n+Iwce$i6UpFdm6y4 zlNy;Lq88@te$25{NmnD`7F(q-2^)W<@CY_%G}Y)SU=DJ@%bk+poZ`ALw^5iu$&f48 znK1^5Kq;F*z?ge99fN=mjztD7^$Erjr(_Ih^>FMd( z??Di!Bm?W-lg#$Nro94!FeRCQQc_YhWppV3?ucNSfS5SF;pJUHX z#V#UKob5lzzFtdyecVuC>K4(Yy}m@@-}5@vP!gEqdAsMk_gIN_0Wo5CXS6OaCv?9U;e4hAe*}{I4UVJ@zI_sYJfBJJA5C*DMmc>cOF6-=; NFJ86i+Cov{zX66w`||(* literal 0 HcmV?d00001 diff --git a/pkg/server/assets/static/manifest.json b/pkg/server/assets/static/manifest.json new file mode 100644 index 00000000..874b25a6 --- /dev/null +++ b/pkg/server/assets/static/manifest.json @@ -0,0 +1,52 @@ +{ + "name": "Dnote", + "short_name": "Dnote", + "icons": [ + { + "src": "ASSET_BASE_PLACEHOLDER\/android-icon-36x36.png", + "sizes": "36x36", + "type": "image\/png", + "density": "0.75" + }, + { + "src": "ASSET_BASE_PLACEHOLDER\/android-icon-48x48.png", + "sizes": "48x48", + "type": "image\/png", + "density": "1.0" + }, + { + "src": "ASSET_BASE_PLACEHOLDER\/android-icon-72x72.png", + "sizes": "72x72", + "type": "image\/png", + "density": "1.5" + }, + { + "src": "ASSET_BASE_PLACEHOLDER\/android-icon-96x96.png", + "sizes": "96x96", + "type": "image\/png", + "density": "2.0" + }, + { + "src": "ASSET_BASE_PLACEHOLDER\/android-icon-144x144.png", + "sizes": "144x144", + "type": "image\/png", + "density": "3.0" + }, + { + "src": "ASSET_BASE_PLACEHOLDER\/android-icon-192x192.png", + "sizes": "192x192", + "type": "image\/png", + "density": "4.0" + }, + { + "src": "ASSET_BASE_PLACEHOLDER\/logo-512x512.png", + "sizes": "512x512", + "type": "image\/png", + "density": "4.0" + } + ], + "start_url": "/", + "display": "standalone", + "background_color": "#072a40", + "theme_color": "#ffffff" +} diff --git a/pkg/server/assets/static/ms-icon-144x144.png b/pkg/server/assets/static/ms-icon-144x144.png new file mode 100644 index 0000000000000000000000000000000000000000..09b28d2f31989bb81fb84e6da6127bb371ae1735 GIT binary patch literal 2448 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 literal 0 HcmV?d00001 diff --git a/pkg/server/assets/static/offline.html b/pkg/server/assets/static/offline.html new file mode 100644 index 00000000..0aadab72 --- /dev/null +++ b/pkg/server/assets/static/offline.html @@ -0,0 +1,41 @@ + + + + + + + Page Not Found | Dnote + + + +
+

You are offline

+

+ Please check you connection and try again. +

+
+ + diff --git a/pkg/server/assets/styles/build.sh b/pkg/server/assets/styles/build.sh new file mode 100755 index 00000000..cde19626 --- /dev/null +++ b/pkg/server/assets/styles/build.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +# build.sh builds styles +set -ex + +dir=$(dirname "${BASH_SOURCE[0]}") +serverDir="$dir/../.." +outputDir="$serverDir/static" +inputDir="$dir/src" + +rm -rf "${outputDir:?}/*" + +"$dir/../node_modules/.bin/sass" --version + +task="$dir/../node_modules/.bin/sass \ + --style compressed \ + --source-map \ + $inputDir:$outputDir" + +# compile first then watch +eval "$task" + +if [[ "$1" == "true" ]]; then + eval "$task --watch --poll" +fi diff --git a/pkg/server/assets/styles/src/_books.scss b/pkg/server/assets/styles/src/_books.scss new file mode 100644 index 00000000..9dcc5a1a --- /dev/null +++ b/pkg/server/assets/styles/src/_books.scss @@ -0,0 +1,11 @@ +.books-page { + .books-content { + padding: rem(16px) rem(24px); + margin-top: rem(16px); + + h1 { + border-bottom: 1px solid $lighter-gray; + margin-bottom: rem(12px); + } + } +} diff --git a/pkg/server/assets/styles/src/_bootstrap.scss b/pkg/server/assets/styles/src/_bootstrap.scss new file mode 100644 index 00000000..ea4a69de --- /dev/null +++ b/pkg/server/assets/styles/src/_bootstrap.scss @@ -0,0 +1,176 @@ +/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd + * + * This file is part of Dnote. + * + * Dnote is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Dnote is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Dnote. If not, see . + */ + +// From Bootstrap <4.3.1 +// MIT Licensed - https://github.com/twbs/bootstrap/blob/master/LICENSE +.form-control { + display: block; + width: 100%; + padding: 0.375rem 0.75rem; + font-size: 1rem; + line-height: 1.5; + color: #495057; + background-color: #fff; + background-clip: padding-box; + border: 1px solid #ced4da; + border-radius: 0.25rem; + transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; +} + +.alert { + position: relative; + padding: 1.75rem 1.25rem; + border: 1px solid transparent; +} + +.alert-heading { + color: inherit; +} + +.alert-link { + font-weight: 700; +} + +.alert-dismissible { + // padding-right: 4rem; +} + +.alert-dismissible .close { + position: absolute; + top: 0; + right: 0; + padding: 0.75rem 1.25rem; + color: inherit; +} + +.alert-primary { + color: #004085; + background-color: #cce5ff; + border-color: #b8daff; +} + +.alert-primary hr { + border-top-color: #9fcdff; +} + +.alert-primary .alert-link { + color: #002752; +} + +.alert-secondary { + color: #383d41; + background-color: #e2e3e5; + border-color: #d6d8db; +} + +.alert-secondary hr { + border-top-color: #c8cbcf; +} + +.alert-secondary .alert-link { + color: #202326; +} + +.alert-success { + color: #155724; + background-color: #d4edda; + border-color: #c3e6cb; +} + +.alert-success hr { + border-top-color: #b1dfbb; +} + +.alert-success .alert-link { + color: #0b2e13; +} + +.alert-info { + color: #0c5460; + background-color: #d1ecf1; + border-color: #bee5eb; +} + +.alert-info hr { + border-top-color: #abdde5; +} + +.alert-info .alert-link { + color: #062c33; +} + +.alert-warning { + color: #856404; + background-color: #fff3cd; + border-color: #ffeeba; +} + +.alert-warning hr { + border-top-color: #ffe8a1; +} + +.alert-warning .alert-link { + color: #533f03; +} + +.alert-danger { + color: #721c24; + background-color: #f8d7da; + border-color: #f5c6cb; +} + +.alert-danger hr { + border-top-color: #f1b0b7; +} + +.alert-danger .alert-link { + color: #491217; +} + +.alert-light { + color: #818182; + background-color: #fefefe; + border-color: #fdfdfe; +} + +.alert-light hr { + border-top-color: #ececf6; +} + +.alert-light .alert-link { + color: #686868; +} + +.alert-dark { + color: #1b1e21; + background-color: #d6d8d9; + border-color: #c6c8ca; +} + +.alert-dark hr { + border-top-color: #b9bbbe; +} + +.alert-dark .alert-link { + color: #040505; +} + +// custom +.alert-slim { + padding: 0.75rem 1.25rem; +} diff --git a/pkg/server/assets/styles/src/_buttons.scss b/pkg/server/assets/styles/src/_buttons.scss new file mode 100644 index 00000000..6178c0ce --- /dev/null +++ b/pkg/server/assets/styles/src/_buttons.scss @@ -0,0 +1,182 @@ +/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd + * + * This file is part of Dnote. + * + * Dnote is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Dnote is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Dnote. If not, see . + */ + +@import './theme'; +@import './rem'; +@import './font'; + +@mixin button($text-color, $background-color) { + color: $text-color; + background-color: $background-color; + + &:not(:disabled):hover { + color: $text-color; + background-color: darken($background-color, 5%); + box-shadow: 0px 0px 4px 2px #cacaca; + } +} + +@mixin button-outline($color, $border-color) { + background: transparent; + color: $color; + + &:not(.button-no-ui) { + border-color: $border-color; + border-width: 2px; + } + + &:not(:disabled):hover { + color: $color; + box-shadow: 0px 0px 4px 2px #cacaca; + } +} + +.button { + position: relative; + display: inline-block; + text-align: center; + white-space: nowrap; + vertical-align: middle; + user-select: none; + border-image: initial; + transition-property: color, box-shadow; + transition-duration: 0.2s; + transition-timing-function: ease-in-out; + + text-decoration: none; + cursor: pointer; + + &:not(.button-no-ui) { + border-width: 1px; + border-style: solid; + border-color: transparent; + } + + &:not(:disabled):hover { + text-decoration: none; + } + + &:disabled { + cursor: not-allowed; + opacity: 0.6; + } + + &:focus { + outline: 2px dotted #9c9c9c; + } +} + +button:disabled { + cursor: not-allowed; + opacity: 0.6; +} + +.button-small { + @include font-size('small'); + padding: rem(4px) rem(12px); +} + +.button-normal { + // @include font-size('small'); + padding: rem(8px) rem(16px); +} + +.button-large { + @include font-size('medium'); + + padding: rem(8px) rem(24px); + + @include breakpoint(md) { + padding: rem(12px) rem(36px); + } + + @include breakpoint(lg) { + padding: rem(12px) rem(48px); + } +} + +.button-xlarge { + @include font-size('x-large'); + + padding: rem(16px) rem(24px); + + @include breakpoint(md) { + padding: rem(12px) rem(36px); + } + + @include breakpoint(lg) { + padding: rem(16px) rem(48px); + } +} + +.button-first { + @include button(#ffffff, #333745); +} + +.button-first-outline { + @include button-outline(#333745, #333745); +} + +.button-second { + @include button($black, $second); +} + +.button-second-outline { + @include button-outline($black, $second); +} + +.button-third { + @include button(#ffffff, $third); +} + +.button-third-outline { + @include button-outline($third, $third); +} + +.button-danger { + @include button-outline($danger-text, $danger-text); + font-weight: 600; +} + +.button-stretch { + width: 100%; +} + +.button ~ .button { + margin-left: rem(12px); +} + +.button-no-ui { + border: none; + background: none; + text-align: left; + cursor: pointer; +} + +.button-no-padding { + padding: 0; +} + +.button-link { + color: $link; + + &:hover { + color: $link-hover; + text-decoration: underline; + } +} diff --git a/pkg/server/assets/styles/src/_font.scss b/pkg/server/assets/styles/src/_font.scss new file mode 100644 index 00000000..1f0b90d5 --- /dev/null +++ b/pkg/server/assets/styles/src/_font.scss @@ -0,0 +1,111 @@ +/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd + * + * This file is part of Dnote. + * + * Dnote is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Dnote is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Dnote. If not, see . + */ + +@import './responsive'; + +$lowDecay: 0.1; +$medDecay: 0.15; +$highDecay: 0.2; + +// font-size is a mixin for pre-defined font-size values in rem. +// It also includes px as a fallback for older browsers. +@mixin font-size($size, $responsive: true) { + $smSizeValue: 16; + $mdSizeValue: 16; + $lgSizeValue: 16; + + @if $size == 'x-small' { + $baseSize: 13; + + $smSizeValue: $baseSize; + $mdSizeValue: $baseSize; + $lgSizeValue: $baseSize; + } @else if $size == 'small' { + $baseSize: 14; + + $smSizeValue: $baseSize; + $mdSizeValue: $baseSize; + $lgSizeValue: $baseSize; + } @else if $size == 'regular' { + $baseSize: 16; + + $smSizeValue: $baseSize * (1 - $lowDecay); + $mdSizeValue: $baseSize * (1 - $lowDecay); + $lgSizeValue: $baseSize; + } @else if $size == 'medium' { + $baseSize: 18; + + $smSizeValue: $baseSize; + $mdSizeValue: $baseSize; + $lgSizeValue: $baseSize; + } @else if $size == 'large' { + $baseSize: 20; + + $smSizeValue: $baseSize; + $mdSizeValue: $baseSize; + $lgSizeValue: $baseSize; + } @else if $size == 'x-large' { + $baseSize: 24; + + $smSizeValue: $baseSize * (1 - $lowDecay * 2); + $mdSizeValue: $baseSize * (1 - $lowDecay); + $lgSizeValue: $baseSize; + } @else if $size == '2x-large' { + $baseSize: 32; + + $smSizeValue: $baseSize * (1 - $lowDecay * 2); + $mdSizeValue: $baseSize * (1 - $lowDecay); + $lgSizeValue: $baseSize; + } @else if $size == '3x-large' { + $baseSize: 36; + + $smSizeValue: $baseSize * (1 - $medDecay * 2); + $mdSizeValue: $baseSize * (1 - $medDecay); + $lgSizeValue: $baseSize; + } @else if $size == '4x-large' { + $baseSize: 48; + + $smSizeValue: $baseSize * (1 - $medDecay * 2); + $mdSizeValue: $baseSize * (1 - $medDecay); + $lgSizeValue: $baseSize; + } @else if $size == '5x-large' { + $baseSize: 56; + + $smSizeValue: $baseSize * (1 - $highDecay * 2); + $mdSizeValue: $baseSize * (1 - $highDecay); + $lgSizeValue: $baseSize; + } + + @if $responsive == true { + font-size: $smSizeValue * 1px; + font-size: $smSizeValue * 0.1rem; + + @include breakpoint(md) { + font-size: $mdSizeValue * 1px; + font-size: $mdSizeValue * 0.1rem; + } + + @include breakpoint(lg) { + font-size: $lgSizeValue * 1px; + font-size: $lgSizeValue * 0.1rem; + } + } @else { + font-size: $lgSizeValue * 1px; + font-size: $lgSizeValue * 0.1rem; + } +} diff --git a/pkg/server/assets/styles/src/_global.scss b/pkg/server/assets/styles/src/_global.scss new file mode 100644 index 00000000..d7650265 --- /dev/null +++ b/pkg/server/assets/styles/src/_global.scss @@ -0,0 +1,85 @@ +.main { + position: relative; + display: flex; + flex-direction: column; + background: $lighter-gray; + min-height: calc(100vh - #{$header-height}); + // margin-bottom: $footer-height; + + &.nofooter { + margin-bottom: 0; + } + + &.noheader:not(.nofooter) { + min-height: calc(100vh - #{$footer-height}); + } + &.nofooter:not(.noheader) { + min-height: calc(100vh - #{$header-height}); + } + &.nofooter.noheader { + min-height: 100vh; + } + + @include breakpoint(lg) { + margin-bottom: 0; + min-height: calc(100vh - #{$header-height}); + } +} + +/* partials */ +.partial--time { + color: $gray; + @include font-size('small'); + + .mobile-text { + @include breakpoint(md) { + display: none; + } + } + .text { + display: none; + + @include breakpoint(md) { + display: inherit; + } + } +} + +.partial--page-toolbar { + @include breakpoint(lg) { + height: rem(48px); + border-radius: rem(4px); + background: $light; + box-shadow: 0 0 8px rgba(0, 0, 0, 0.14); + + &.bottom { + margin-top: rem(12px); + } + } +} + +/* icons */ +.icon--caret-right { + transform: rotate(-90deg); +} + +.icon--caret-left { + transform: rotate(90deg); +} + +// was originally used in note show +.frame { + box-shadow: 0 1px 5px rgba(0, 0, 0, 0.2); + background: white; + + &.collapsed { + .book-label { + // control the coloro of ellipsis when overflown + // color: $light-gray; + } + + .book-label a { + // color: $light-gray; + } + } +} diff --git a/pkg/server/assets/styles/src/_grid.scss b/pkg/server/assets/styles/src/_grid.scss new file mode 100644 index 00000000..66e07a28 --- /dev/null +++ b/pkg/server/assets/styles/src/_grid.scss @@ -0,0 +1,1108 @@ +/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd + * + * This file is part of Dnote. + * + * Dnote is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Dnote is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Dnote. If not, see . + */ + +/*! + * Bootstrap Grid v4.3.1 (https://getbootstrap.com/) + * Copyright 2011-2019 The Bootstrap Authors + * Copyright 2011-2019 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +html { + box-sizing: border-box; + -ms-overflow-style: scrollbar; +} + +*, +*::before, +*::after { + box-sizing: inherit; +} + +.container-wide { + width: 100%; + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} + +@media (min-width: 576px) { + .container-wide { + max-width: 540px; + } +} + +@media (min-width: 768px) { + .container-wide { + max-width: 720px; + } +} + +@media (min-width: 992px) { + .container-wide { + max-width: 960px; + } +} + +@media (min-width: 1200px) { + .container-wide { + max-width: 1040px; + } +} + +@media (min-width: 1440px) { + .container-wide { + max-width: 1280px; + } +} + +@media (min-width: 1800px) { + .container-wide { + max-width: 1660px; + } +} + +.container-fluid { + width: 100%; + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} + +.container { + width: 100%; + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} + +@media (min-width: 576px) { + .container { + max-width: 540px; + } +} + +@media (min-width: 768px) { + .container { + max-width: 720px; + } +} + +@media (min-width: 992px) { + .container { + max-width: 960px; + } +} + +@media (min-width: 1200px) { + .container { + max-width: 1280px; + } +} + +.row { + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + margin-right: -15px; + margin-left: -15px; +} + +.no-gutters { + margin-right: 0; + margin-left: 0; +} + +.no-gutters > .col, +.no-gutters > [class*='col-'] { + padding-right: 0; + padding-left: 0; +} + +.col-1, +.col-2, +.col-3, +.col-4, +.col-5, +.col-6, +.col-7, +.col-8, +.col-9, +.col-10, +.col-11, +.col-12, +.col, +.col-auto, +.col-sm-1, +.col-sm-2, +.col-sm-3, +.col-sm-4, +.col-sm-5, +.col-sm-6, +.col-sm-7, +.col-sm-8, +.col-sm-9, +.col-sm-10, +.col-sm-11, +.col-sm-12, +.col-sm, +.col-sm-auto, +.col-md-1, +.col-md-2, +.col-md-3, +.col-md-4, +.col-md-5, +.col-md-6, +.col-md-7, +.col-md-8, +.col-md-9, +.col-md-10, +.col-md-11, +.col-md-12, +.col-md, +.col-md-auto, +.col-lg-1, +.col-lg-2, +.col-lg-3, +.col-lg-4, +.col-lg-5, +.col-lg-6, +.col-lg-7, +.col-lg-8, +.col-lg-9, +.col-lg-10, +.col-lg-11, +.col-lg-12, +.col-lg, +.col-lg-auto, +.col-xl-1, +.col-xl-2, +.col-xl-3, +.col-xl-4, +.col-xl-5, +.col-xl-6, +.col-xl-7, +.col-xl-8, +.col-xl-9, +.col-xl-10, +.col-xl-11, +.col-xl-12, +.col-xl, +.col-xl-auto { + position: relative; + width: 100%; + padding-right: 15px; + padding-left: 15px; +} + +.col { + -ms-flex-preferred-size: 0; + flex-basis: 0; + -ms-flex-positive: 1; + flex-grow: 1; + max-width: 100%; +} + +.col-auto { + -ms-flex: 0 0 auto; + flex: 0 0 auto; + width: auto; + max-width: 100%; +} + +.col-1 { + -ms-flex: 0 0 8.333333%; + flex: 0 0 8.333333%; + max-width: 8.333333%; +} + +.col-2 { + -ms-flex: 0 0 16.666667%; + flex: 0 0 16.666667%; + max-width: 16.666667%; +} + +.col-3 { + -ms-flex: 0 0 25%; + flex: 0 0 25%; + max-width: 25%; +} + +.col-4 { + -ms-flex: 0 0 33.333333%; + flex: 0 0 33.333333%; + max-width: 33.333333%; +} + +.col-5 { + -ms-flex: 0 0 41.666667%; + flex: 0 0 41.666667%; + max-width: 41.666667%; +} + +.col-6 { + -ms-flex: 0 0 50%; + flex: 0 0 50%; + max-width: 50%; +} + +.col-7 { + -ms-flex: 0 0 58.333333%; + flex: 0 0 58.333333%; + max-width: 58.333333%; +} + +.col-8 { + -ms-flex: 0 0 66.666667%; + flex: 0 0 66.666667%; + max-width: 66.666667%; +} + +.col-9 { + -ms-flex: 0 0 75%; + flex: 0 0 75%; + max-width: 75%; +} + +.col-10 { + -ms-flex: 0 0 83.333333%; + flex: 0 0 83.333333%; + max-width: 83.333333%; +} + +.col-11 { + -ms-flex: 0 0 91.666667%; + flex: 0 0 91.666667%; + max-width: 91.666667%; +} + +.col-12 { + -ms-flex: 0 0 100%; + flex: 0 0 100%; + max-width: 100%; +} + +.order-first { + -ms-flex-order: -1; + order: -1; +} + +.order-last { + -ms-flex-order: 13; + order: 13; +} + +.order-0 { + -ms-flex-order: 0; + order: 0; +} + +.order-1 { + -ms-flex-order: 1; + order: 1; +} + +.order-2 { + -ms-flex-order: 2; + order: 2; +} + +.order-3 { + -ms-flex-order: 3; + order: 3; +} + +.order-4 { + -ms-flex-order: 4; + order: 4; +} + +.order-5 { + -ms-flex-order: 5; + order: 5; +} + +.order-6 { + -ms-flex-order: 6; + order: 6; +} + +.order-7 { + -ms-flex-order: 7; + order: 7; +} + +.order-8 { + -ms-flex-order: 8; + order: 8; +} + +.order-9 { + -ms-flex-order: 9; + order: 9; +} + +.order-10 { + -ms-flex-order: 10; + order: 10; +} + +.order-11 { + -ms-flex-order: 11; + order: 11; +} + +.order-12 { + -ms-flex-order: 12; + order: 12; +} + +.offset-1 { + margin-left: 8.333333%; +} + +.offset-2 { + margin-left: 16.666667%; +} + +.offset-3 { + margin-left: 25%; +} + +.offset-4 { + margin-left: 33.333333%; +} + +.offset-5 { + margin-left: 41.666667%; +} + +.offset-6 { + margin-left: 50%; +} + +.offset-7 { + margin-left: 58.333333%; +} + +.offset-8 { + margin-left: 66.666667%; +} + +.offset-9 { + margin-left: 75%; +} + +.offset-10 { + margin-left: 83.333333%; +} + +.offset-11 { + margin-left: 91.666667%; +} + +@media (min-width: 576px) { + .col-sm { + -ms-flex-preferred-size: 0; + flex-basis: 0; + -ms-flex-positive: 1; + flex-grow: 1; + max-width: 100%; + } + .col-sm-auto { + -ms-flex: 0 0 auto; + flex: 0 0 auto; + width: auto; + max-width: 100%; + } + .col-sm-1 { + -ms-flex: 0 0 8.333333%; + flex: 0 0 8.333333%; + max-width: 8.333333%; + } + .col-sm-2 { + -ms-flex: 0 0 16.666667%; + flex: 0 0 16.666667%; + max-width: 16.666667%; + } + .col-sm-3 { + -ms-flex: 0 0 25%; + flex: 0 0 25%; + max-width: 25%; + } + .col-sm-4 { + -ms-flex: 0 0 33.333333%; + flex: 0 0 33.333333%; + max-width: 33.333333%; + } + .col-sm-5 { + -ms-flex: 0 0 41.666667%; + flex: 0 0 41.666667%; + max-width: 41.666667%; + } + .col-sm-6 { + -ms-flex: 0 0 50%; + flex: 0 0 50%; + max-width: 50%; + } + .col-sm-7 { + -ms-flex: 0 0 58.333333%; + flex: 0 0 58.333333%; + max-width: 58.333333%; + } + .col-sm-8 { + -ms-flex: 0 0 66.666667%; + flex: 0 0 66.666667%; + max-width: 66.666667%; + } + .col-sm-9 { + -ms-flex: 0 0 75%; + flex: 0 0 75%; + max-width: 75%; + } + .col-sm-10 { + -ms-flex: 0 0 83.333333%; + flex: 0 0 83.333333%; + max-width: 83.333333%; + } + .col-sm-11 { + -ms-flex: 0 0 91.666667%; + flex: 0 0 91.666667%; + max-width: 91.666667%; + } + .col-sm-12 { + -ms-flex: 0 0 100%; + flex: 0 0 100%; + max-width: 100%; + } + .order-sm-first { + -ms-flex-order: -1; + order: -1; + } + .order-sm-last { + -ms-flex-order: 13; + order: 13; + } + .order-sm-0 { + -ms-flex-order: 0; + order: 0; + } + .order-sm-1 { + -ms-flex-order: 1; + order: 1; + } + .order-sm-2 { + -ms-flex-order: 2; + order: 2; + } + .order-sm-3 { + -ms-flex-order: 3; + order: 3; + } + .order-sm-4 { + -ms-flex-order: 4; + order: 4; + } + .order-sm-5 { + -ms-flex-order: 5; + order: 5; + } + .order-sm-6 { + -ms-flex-order: 6; + order: 6; + } + .order-sm-7 { + -ms-flex-order: 7; + order: 7; + } + .order-sm-8 { + -ms-flex-order: 8; + order: 8; + } + .order-sm-9 { + -ms-flex-order: 9; + order: 9; + } + .order-sm-10 { + -ms-flex-order: 10; + order: 10; + } + .order-sm-11 { + -ms-flex-order: 11; + order: 11; + } + .order-sm-12 { + -ms-flex-order: 12; + order: 12; + } + .offset-sm-0 { + margin-left: 0; + } + .offset-sm-1 { + margin-left: 8.333333%; + } + .offset-sm-2 { + margin-left: 16.666667%; + } + .offset-sm-3 { + margin-left: 25%; + } + .offset-sm-4 { + margin-left: 33.333333%; + } + .offset-sm-5 { + margin-left: 41.666667%; + } + .offset-sm-6 { + margin-left: 50%; + } + .offset-sm-7 { + margin-left: 58.333333%; + } + .offset-sm-8 { + margin-left: 66.666667%; + } + .offset-sm-9 { + margin-left: 75%; + } + .offset-sm-10 { + margin-left: 83.333333%; + } + .offset-sm-11 { + margin-left: 91.666667%; + } +} + +@media (min-width: 768px) { + .col-md { + -ms-flex-preferred-size: 0; + flex-basis: 0; + -ms-flex-positive: 1; + flex-grow: 1; + max-width: 100%; + } + .col-md-auto { + -ms-flex: 0 0 auto; + flex: 0 0 auto; + width: auto; + max-width: 100%; + } + .col-md-1 { + -ms-flex: 0 0 8.333333%; + flex: 0 0 8.333333%; + max-width: 8.333333%; + } + .col-md-2 { + -ms-flex: 0 0 16.666667%; + flex: 0 0 16.666667%; + max-width: 16.666667%; + } + .col-md-3 { + -ms-flex: 0 0 25%; + flex: 0 0 25%; + max-width: 25%; + } + .col-md-4 { + -ms-flex: 0 0 33.333333%; + flex: 0 0 33.333333%; + max-width: 33.333333%; + } + .col-md-5 { + -ms-flex: 0 0 41.666667%; + flex: 0 0 41.666667%; + max-width: 41.666667%; + } + .col-md-6 { + -ms-flex: 0 0 50%; + flex: 0 0 50%; + max-width: 50%; + } + .col-md-7 { + -ms-flex: 0 0 58.333333%; + flex: 0 0 58.333333%; + max-width: 58.333333%; + } + .col-md-8 { + -ms-flex: 0 0 66.666667%; + flex: 0 0 66.666667%; + max-width: 66.666667%; + } + .col-md-9 { + -ms-flex: 0 0 75%; + flex: 0 0 75%; + max-width: 75%; + } + .col-md-10 { + -ms-flex: 0 0 83.333333%; + flex: 0 0 83.333333%; + max-width: 83.333333%; + } + .col-md-11 { + -ms-flex: 0 0 91.666667%; + flex: 0 0 91.666667%; + max-width: 91.666667%; + } + .col-md-12 { + -ms-flex: 0 0 100%; + flex: 0 0 100%; + max-width: 100%; + } + .order-md-first { + -ms-flex-order: -1; + order: -1; + } + .order-md-last { + -ms-flex-order: 13; + order: 13; + } + .order-md-0 { + -ms-flex-order: 0; + order: 0; + } + .order-md-1 { + -ms-flex-order: 1; + order: 1; + } + .order-md-2 { + -ms-flex-order: 2; + order: 2; + } + .order-md-3 { + -ms-flex-order: 3; + order: 3; + } + .order-md-4 { + -ms-flex-order: 4; + order: 4; + } + .order-md-5 { + -ms-flex-order: 5; + order: 5; + } + .order-md-6 { + -ms-flex-order: 6; + order: 6; + } + .order-md-7 { + -ms-flex-order: 7; + order: 7; + } + .order-md-8 { + -ms-flex-order: 8; + order: 8; + } + .order-md-9 { + -ms-flex-order: 9; + order: 9; + } + .order-md-10 { + -ms-flex-order: 10; + order: 10; + } + .order-md-11 { + -ms-flex-order: 11; + order: 11; + } + .order-md-12 { + -ms-flex-order: 12; + order: 12; + } + .offset-md-0 { + margin-left: 0; + } + .offset-md-1 { + margin-left: 8.333333%; + } + .offset-md-2 { + margin-left: 16.666667%; + } + .offset-md-3 { + margin-left: 25%; + } + .offset-md-4 { + margin-left: 33.333333%; + } + .offset-md-5 { + margin-left: 41.666667%; + } + .offset-md-6 { + margin-left: 50%; + } + .offset-md-7 { + margin-left: 58.333333%; + } + .offset-md-8 { + margin-left: 66.666667%; + } + .offset-md-9 { + margin-left: 75%; + } + .offset-md-10 { + margin-left: 83.333333%; + } + .offset-md-11 { + margin-left: 91.666667%; + } +} + +@media (min-width: 992px) { + .col-lg { + -ms-flex-preferred-size: 0; + flex-basis: 0; + -ms-flex-positive: 1; + flex-grow: 1; + max-width: 100%; + } + .col-lg-auto { + -ms-flex: 0 0 auto; + flex: 0 0 auto; + width: auto; + max-width: 100%; + } + .col-lg-1 { + -ms-flex: 0 0 8.333333%; + flex: 0 0 8.333333%; + max-width: 8.333333%; + } + .col-lg-2 { + -ms-flex: 0 0 16.666667%; + flex: 0 0 16.666667%; + max-width: 16.666667%; + } + .col-lg-3 { + -ms-flex: 0 0 25%; + flex: 0 0 25%; + max-width: 25%; + } + .col-lg-4 { + -ms-flex: 0 0 33.333333%; + flex: 0 0 33.333333%; + max-width: 33.333333%; + } + .col-lg-5 { + -ms-flex: 0 0 41.666667%; + flex: 0 0 41.666667%; + max-width: 41.666667%; + } + .col-lg-6 { + -ms-flex: 0 0 50%; + flex: 0 0 50%; + max-width: 50%; + } + .col-lg-7 { + -ms-flex: 0 0 58.333333%; + flex: 0 0 58.333333%; + max-width: 58.333333%; + } + .col-lg-8 { + -ms-flex: 0 0 66.666667%; + flex: 0 0 66.666667%; + max-width: 66.666667%; + } + .col-lg-9 { + -ms-flex: 0 0 75%; + flex: 0 0 75%; + max-width: 75%; + } + .col-lg-10 { + -ms-flex: 0 0 83.333333%; + flex: 0 0 83.333333%; + max-width: 83.333333%; + } + .col-lg-11 { + -ms-flex: 0 0 91.666667%; + flex: 0 0 91.666667%; + max-width: 91.666667%; + } + .col-lg-12 { + -ms-flex: 0 0 100%; + flex: 0 0 100%; + max-width: 100%; + } + .order-lg-first { + -ms-flex-order: -1; + order: -1; + } + .order-lg-last { + -ms-flex-order: 13; + order: 13; + } + .order-lg-0 { + -ms-flex-order: 0; + order: 0; + } + .order-lg-1 { + -ms-flex-order: 1; + order: 1; + } + .order-lg-2 { + -ms-flex-order: 2; + order: 2; + } + .order-lg-3 { + -ms-flex-order: 3; + order: 3; + } + .order-lg-4 { + -ms-flex-order: 4; + order: 4; + } + .order-lg-5 { + -ms-flex-order: 5; + order: 5; + } + .order-lg-6 { + -ms-flex-order: 6; + order: 6; + } + .order-lg-7 { + -ms-flex-order: 7; + order: 7; + } + .order-lg-8 { + -ms-flex-order: 8; + order: 8; + } + .order-lg-9 { + -ms-flex-order: 9; + order: 9; + } + .order-lg-10 { + -ms-flex-order: 10; + order: 10; + } + .order-lg-11 { + -ms-flex-order: 11; + order: 11; + } + .order-lg-12 { + -ms-flex-order: 12; + order: 12; + } + .offset-lg-0 { + margin-left: 0; + } + .offset-lg-1 { + margin-left: 8.333333%; + } + .offset-lg-2 { + margin-left: 16.666667%; + } + .offset-lg-3 { + margin-left: 25%; + } + .offset-lg-4 { + margin-left: 33.333333%; + } + .offset-lg-5 { + margin-left: 41.666667%; + } + .offset-lg-6 { + margin-left: 50%; + } + .offset-lg-7 { + margin-left: 58.333333%; + } + .offset-lg-8 { + margin-left: 66.666667%; + } + .offset-lg-9 { + margin-left: 75%; + } + .offset-lg-10 { + margin-left: 83.333333%; + } + .offset-lg-11 { + margin-left: 91.666667%; + } +} + +@media (min-width: 1200px) { + .col-xl { + -ms-flex-preferred-size: 0; + flex-basis: 0; + -ms-flex-positive: 1; + flex-grow: 1; + max-width: 100%; + } + .col-xl-auto { + -ms-flex: 0 0 auto; + flex: 0 0 auto; + width: auto; + max-width: 100%; + } + .col-xl-1 { + -ms-flex: 0 0 8.333333%; + flex: 0 0 8.333333%; + max-width: 8.333333%; + } + .col-xl-2 { + -ms-flex: 0 0 16.666667%; + flex: 0 0 16.666667%; + max-width: 16.666667%; + } + .col-xl-3 { + -ms-flex: 0 0 25%; + flex: 0 0 25%; + max-width: 25%; + } + .col-xl-4 { + -ms-flex: 0 0 33.333333%; + flex: 0 0 33.333333%; + max-width: 33.333333%; + } + .col-xl-5 { + -ms-flex: 0 0 41.666667%; + flex: 0 0 41.666667%; + max-width: 41.666667%; + } + .col-xl-6 { + -ms-flex: 0 0 50%; + flex: 0 0 50%; + max-width: 50%; + } + .col-xl-7 { + -ms-flex: 0 0 58.333333%; + flex: 0 0 58.333333%; + max-width: 58.333333%; + } + .col-xl-8 { + -ms-flex: 0 0 66.666667%; + flex: 0 0 66.666667%; + max-width: 66.666667%; + } + .col-xl-9 { + -ms-flex: 0 0 75%; + flex: 0 0 75%; + max-width: 75%; + } + .col-xl-10 { + -ms-flex: 0 0 83.333333%; + flex: 0 0 83.333333%; + max-width: 83.333333%; + } + .col-xl-11 { + -ms-flex: 0 0 91.666667%; + flex: 0 0 91.666667%; + max-width: 91.666667%; + } + .col-xl-12 { + -ms-flex: 0 0 100%; + flex: 0 0 100%; + max-width: 100%; + } + .order-xl-first { + -ms-flex-order: -1; + order: -1; + } + .order-xl-last { + -ms-flex-order: 13; + order: 13; + } + .order-xl-0 { + -ms-flex-order: 0; + order: 0; + } + .order-xl-1 { + -ms-flex-order: 1; + order: 1; + } + .order-xl-2 { + -ms-flex-order: 2; + order: 2; + } + .order-xl-3 { + -ms-flex-order: 3; + order: 3; + } + .order-xl-4 { + -ms-flex-order: 4; + order: 4; + } + .order-xl-5 { + -ms-flex-order: 5; + order: 5; + } + .order-xl-6 { + -ms-flex-order: 6; + order: 6; + } + .order-xl-7 { + -ms-flex-order: 7; + order: 7; + } + .order-xl-8 { + -ms-flex-order: 8; + order: 8; + } + .order-xl-9 { + -ms-flex-order: 9; + order: 9; + } + .order-xl-10 { + -ms-flex-order: 10; + order: 10; + } + .order-xl-11 { + -ms-flex-order: 11; + order: 11; + } + .order-xl-12 { + -ms-flex-order: 12; + order: 12; + } + .offset-xl-0 { + margin-left: 0; + } + .offset-xl-1 { + margin-left: 8.333333%; + } + .offset-xl-2 { + margin-left: 16.666667%; + } + .offset-xl-3 { + margin-left: 25%; + } + .offset-xl-4 { + margin-left: 33.333333%; + } + .offset-xl-5 { + margin-left: 41.666667%; + } + .offset-xl-6 { + margin-left: 50%; + } + .offset-xl-7 { + margin-left: 58.333333%; + } + .offset-xl-8 { + margin-left: 66.666667%; + } + .offset-xl-9 { + margin-left: 75%; + } + .offset-xl-10 { + margin-left: 83.333333%; + } + .offset-xl-11 { + margin-left: 91.666667%; + } +} diff --git a/pkg/server/assets/styles/src/_header.scss b/pkg/server/assets/styles/src/_header.scss new file mode 100644 index 00000000..fbd971b5 --- /dev/null +++ b/pkg/server/assets/styles/src/_header.scss @@ -0,0 +1,192 @@ +@import './theme'; +@import './variables'; + +.header-wrapper { + padding: 0; + z-index: 2; + position: relative; + display: flex; + box-shadow: 0 1px 5px rgba(0, 0, 0, 0.2); + background: $first; + align-items: stretch; + justify-content: space-between; + flex: 1; + flex-direction: column; + position: sticky; + top: 0; + z-index: 4; + height: $header-height; + + .container { + height: 100%; + } + + @include breakpoint(md) { + flex-direction: row; + } + + .header-content { + display: flex; + justify-content: space-between; + height: 100%; + } + + .left { + display: flex; + } + + .right { + display: flex; + } + + .search-wrapper { + align-items: center; + display: flex; + margin-left: rem(32px); + } + + .search-input { + width: rem(356px); + border: 0; + padding: 4px 12px; + border-radius: rem(4px); + @include font-size('small'); + } + + .brand { + display: flex; + align-items: center; + + &:hover { + text-decoration: none; + } + } + + .main-nav { + margin-left: rem(32px); + display: flex; + + .list { + display: flex; + } + + .item { + display: flex; + align-items: stretch; + } + + .nav-link { + @include font-size('small'); + display: flex; + font-weight: 600; + align-items: center; + padding: 0 rem(16px); + color: $white; + + &:hover { + color: $white; + text-decoration: none; + background: lighten($first, 10%); + } + } + + .nav-item { + @include font-size('small'); + font-weight: 600; + } + } + + .dropdown-trigger { + color: white; + padding: 16px; + font-size: 16px; + border: none; + cursor: pointer; + } + + .dropdown { + position: relative; + display: inline-block; + } + + .dropdown-content { + display: none; + position: absolute; + background-color: #f1f1f1; + width: rem(240px); + background: #fff; + border: 1px solid #d8d8d8; + border-radius: 4px; + box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); + top: calc(100% + 4px); + z-index: 1; + + &.show { + display: block; + } + + &.right-align { + right: 0; + } + } + + .account-dropdown { + .dropdown-trigger { + height: 100%; + } + + .account-dropdown-header { + @include font-size('small'); + color: $light-gray; + padding: rem(8px) rem(12px); + display: block; + margin-bottom: 0; + white-space: nowrap; + + svg { + fill: $light-gray; + } + + .email { + font-weight: 600; + white-space: normal; + word-break: break-all; + } + } + + .dropdown-link { + @include font-size('small'); + white-space: pre; + padding: rem(8px) rem(14px); + width: 100%; + display: block; + color: black; + + &:hover { + background: $lighter-gray; + text-decoration: none; + color: #0056b3; + } + + &.disabled { + color: #d4d4d4; + cursor: not-allowed; + } + + &:not(.disabled):focus { + background: $lighter-gray; + color: #0056b3; + outline: 1px dotted gray; + } + } + + .session-notice-wrapper { + display: flex; + align-items: center; + } + + .session-notice { + margin-left: rem(4px); + } + } +} diff --git a/pkg/server/assets/styles/src/_hljs.scss b/pkg/server/assets/styles/src/_hljs.scss new file mode 100644 index 00000000..03636f1a --- /dev/null +++ b/pkg/server/assets/styles/src/_hljs.scss @@ -0,0 +1,147 @@ +/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd + * + * This file is part of Dnote. + * + * Dnote is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Dnote is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Dnote. If not, see . + */ + +/* +highlight.js + +BSD 3-Clause License + +Copyright (c) 2006, Ivan Sagalaev. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// github style + +.hljs { + display: block; + overflow-x: auto; + padding: 0.5em; + color: #333; + background: #f8f8f8; +} + +.hljs-comment, +.hljs-quote { + color: #998; + font-style: italic; +} + +.hljs-keyword, +.hljs-selector-tag, +.hljs-subst { + color: #333; + font-weight: bold; +} + +.hljs-number, +.hljs-literal, +.hljs-variable, +.hljs-template-variable, +.hljs-tag .hljs-attr { + color: #008080; +} + +.hljs-string, +.hljs-doctag { + color: #d14; +} + +.hljs-title, +.hljs-section, +.hljs-selector-id { + color: #900; + font-weight: bold; +} + +.hljs-subst { + font-weight: normal; +} + +.hljs-type, +.hljs-class .hljs-title { + color: #458; + font-weight: bold; +} + +.hljs-tag, +.hljs-name, +.hljs-attribute { + color: #000080; + font-weight: normal; +} + +.hljs-regexp, +.hljs-link { + color: #009926; +} + +.hljs-symbol, +.hljs-bullet { + color: #990073; +} + +.hljs-built_in, +.hljs-builtin-name { + color: #0086b3; +} + +.hljs-meta { + color: #999; + font-weight: bold; +} + +.hljs-deletion { + background: #fdd; +} + +.hljs-addition { + background: #dfd; +} + +.hljs-emphasis { + font-style: italic; +} + +.hljs-strong { + font-weight: bold; +} diff --git a/pkg/server/assets/styles/src/_home.scss b/pkg/server/assets/styles/src/_home.scss new file mode 100644 index 00000000..282589d2 --- /dev/null +++ b/pkg/server/assets/styles/src/_home.scss @@ -0,0 +1,185 @@ +@import './theme'; +@import './font'; + +.home-page { + .note-group-list { + flex-grow: 1; + + @include breakpoint(lg) { + margin-top: rem(16px); + } + + .note-group-list-empty { + padding: rem(40px) rem(16px); + text-align: center; + color: $gray; + } + } + + .note-group { + position: relative; + border-radius: 4px; + box-shadow: 0 0 8px rgba(0, 0, 0, 0.14); + + &:not(:first-of-type) { + margin-top: rem(20px); + + @include breakpoint(md) { + margin-top: rem(24px); + } + } + + .note-group-header { + @include font-size('small'); + display: flex; + justify-content: space-between; + color: white; + padding: rem(12px) rem(16px); + background: $light; + color: $black; + border-bottom: 1px solid $border-color; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + } + + .date { + font-weight: 600; + @include font-size('small'); + } + + .mask { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + background: white; + z-index: 1; + opacity: 0.8; + } + + .header-date { + font-weight: 600; + @include font-size('regular'); + } + .header-count { + font-weight: 300; + } + + .list { + list-style: none; + padding-left: 0; + margin-bottom: 0; + } + } + + .note-list { + list-style: none; + padding-left: 0; + margin-bottom: 0; + } + + .note-item { + background: white; + position: relative; + + border-bottom: 1px solid $border-color; + + .link { + color: $black; + display: block; + padding: rem(12px) rem(16px); + border: 2px solid transparent; + + &:hover { + text-decoration: none; + background: $light-blue; + color: inherit; + } + } + + .meta { + line-height: rem(16px); + } + + .body { + overflow: hidden; + text-overflow: ellipsis; + } + + .note-header { + display: flex; + justify-content: space-between; + } + + .note-content { + margin-top: rem(12px); + line-height: 1.6rem; + overflow: hidden; + text-overflow: ellipsis; + color: $gray; + } + + .book-label { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-weight: 700; + @include font-size('small'); + + width: 212px; + + @include breakpoint('md') { + width: 320px; + } + } + + .match { + display: inline-block; + background: #f7f77d; + padding: rem(4px) rem(4px); + } + } + + .toolbar { + text-align: right; + } + + .paginator { + display: inline-flex; + align-items: center; + + .paginator-info { + @include font-size('small'); + color: $gray; + } + + .paginator-link { + padding: rem(12px) rem(12px); + + &.disabled { + cursor: not-allowed; + } + } + + .paginator-link-prev { + margin-left: rem(8px); + + @include breakpoint(md) { + margin-left: rem(20px); + } + } + + .caret-next { + transform: rotate(-90deg); + } + + .caret-prev { + transform: rotate(90deg); + } + + .paginator-label { + font-weight: 600; + } + } +} diff --git a/pkg/server/assets/styles/src/_login.scss b/pkg/server/assets/styles/src/_login.scss new file mode 100644 index 00000000..673813e3 --- /dev/null +++ b/pkg/server/assets/styles/src/_login.scss @@ -0,0 +1,88 @@ +@import './theme'; +@import './font'; + +.auth-page { + background: $lighter-gray; + text-align: center; + min-height: 100vh; + padding: 50px 0; + + .auth-button { + margin-top: 8px; + } + + .heading { + color: $black; + @include font-size('2x-large'); + font-weight: 300; + margin-top: 12px; + margin-bottom: 0; + } + + .body { + max-width: 420px; + margin-left: auto; + margin-right: auto; + margin-top: 20px; + } + + .referrer-flash { + margin: 24px 0; + } + .error-flash { + margin-bottom: 24px; + } + + .footer { + margin-top: 20px; + line-height: 20px; + } + + .callout { + color: #7c7c7c; + @include font-size('small'); + } + .cta { + @include font-size('small'); + } + + .panel { + border: 1px solid $border-color; + background: $white; + border-radius: 2px; + padding: 20px; + text-align: left; + } + + .auth-button { + margin-top: 16px; + } + + .input-row { + & ~ .input-row { + margin-top: 12px; + } + } + .label { + @include font-size('small'); + font-weight: 600; + width: 100%; + margin-bottom: 0; + } + + .forgot { + @include font-size('small'); + float: right; + font-weight: 400; + } + + &.password-reset-page { + .email-input { + margin-top: rem(16px); + } + } + + .alert { + margin-bottom: 1rem; + } +} diff --git a/pkg/server/assets/styles/src/_markdown.scss b/pkg/server/assets/styles/src/_markdown.scss new file mode 100644 index 00000000..99b3e92f --- /dev/null +++ b/pkg/server/assets/styles/src/_markdown.scss @@ -0,0 +1,966 @@ +/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd + * + * This file is part of Dnote. + * + * Dnote is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Dnote is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Dnote. If not, see . + */ + +/* +MIT License + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +.markdown-body .anchor { + float: left; + line-height: 1; + margin-left: -20px; + padding-right: 4px; +} + +.markdown-body .anchor:focus { + outline: none; +} + +.markdown-body h1:hover .anchor, +.markdown-body h2:hover .anchor, +.markdown-body h3:hover .anchor, +.markdown-body h4:hover .anchor, +.markdown-body h5:hover .anchor, +.markdown-body h6:hover .anchor { + text-decoration: none; +} + +.markdown-body { + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust: 100%; + color: #24292e; + line-height: 1.5; + font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, + sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol; + font-size: 16px; + line-height: 1.5; + word-wrap: break-word; +} + +.markdown-body .pl-c { + color: #6a737d; +} + +.markdown-body .pl-c1, +.markdown-body .pl-s .pl-v { + color: #005cc5; +} + +.markdown-body .pl-e, +.markdown-body .pl-en { + color: #6f42c1; +} + +.markdown-body .pl-s .pl-s1, +.markdown-body .pl-smi { + color: #24292e; +} + +.markdown-body .pl-ent { + color: #22863a; +} + +.markdown-body .pl-k { + color: #d73a49; +} + +.markdown-body .pl-pds, +.markdown-body .pl-s, +.markdown-body .pl-s .pl-pse .pl-s1, +.markdown-body .pl-sr, +.markdown-body .pl-sr .pl-cce, +.markdown-body .pl-sr .pl-sra, +.markdown-body .pl-sr .pl-sre { + color: #032f62; +} + +.markdown-body .pl-smw, +.markdown-body .pl-v { + color: #e36209; +} + +.markdown-body .pl-bu { + color: #b31d28; +} + +.markdown-body .pl-ii { + background-color: #b31d28; + color: #fafbfc; +} + +.markdown-body .pl-c2 { + background-color: #d73a49; + color: #fafbfc; +} + +.markdown-body .pl-c2:before { + content: '^M'; +} + +.markdown-body .pl-sr .pl-cce { + color: #22863a; + font-weight: 700; +} + +.markdown-body .pl-ml { + color: #735c0f; +} + +.markdown-body .pl-mh, +.markdown-body .pl-mh .pl-en, +.markdown-body .pl-ms { + color: #005cc5; + font-weight: 700; +} + +.markdown-body .pl-mi { + color: #24292e; + font-style: italic; +} + +.markdown-body .pl-mb { + color: #24292e; + font-weight: 700; +} + +.markdown-body .pl-md { + background-color: #ffeef0; + color: #b31d28; +} + +.markdown-body .pl-mi1 { + background-color: #f0fff4; + color: #22863a; +} + +.markdown-body .pl-mc { + background-color: #ffebda; + color: #e36209; +} + +.markdown-body .pl-mi2 { + background-color: #005cc5; + color: #f6f8fa; +} + +.markdown-body .pl-mdr { + color: #6f42c1; + font-weight: 700; +} + +.markdown-body .pl-ba { + color: #586069; +} + +.markdown-body .pl-sg { + color: #959da5; +} + +.markdown-body .pl-corl { + color: #032f62; + text-decoration: underline; +} + +.markdown-body details { + display: block; +} + +.markdown-body summary { + display: list-item; +} + +.markdown-body a { + background-color: transparent; +} + +.markdown-body a:active, +.markdown-body a:hover { + outline-width: 0; +} + +.markdown-body strong { + font-weight: inherit; + font-weight: bolder; +} + +.markdown-body h1 { + font-size: 2em; + margin: 0.67em 0; +} + +.markdown-body img { + border-style: none; +} + +.markdown-body code, +.markdown-body kbd, +.markdown-body pre { + font-family: monospace, monospace; + font-size: 1em; +} + +.markdown-body hr { + box-sizing: content-box; + height: 0; + overflow: visible; +} + +.markdown-body input { + font: inherit; + margin: 0; +} + +.markdown-body input { + overflow: visible; +} + +.markdown-body [type='checkbox'] { + box-sizing: border-box; + padding: 0; +} + +.markdown-body * { + box-sizing: border-box; +} + +.markdown-body input { + font-family: inherit; + font-size: inherit; + line-height: inherit; +} + +.markdown-body a { + color: #0366d6; + text-decoration: none; +} + +.markdown-body a:hover { + text-decoration: underline; +} + +.markdown-body strong { + font-weight: 600; +} + +.markdown-body hr { + background: transparent; + border: 0; + border-bottom: 1px solid #dfe2e5; + height: 0; + margin: 15px 0; + overflow: hidden; +} + +.markdown-body hr:before { + content: ''; + display: table; +} + +.markdown-body hr:after { + clear: both; + content: ''; + display: table; +} + +.markdown-body table { + border-collapse: collapse; + border-spacing: 0; +} + +.markdown-body td, +.markdown-body th { + padding: 0; +} + +.markdown-body details summary { + cursor: pointer; +} + +.markdown-body h1, +.markdown-body h2, +.markdown-body h3, +.markdown-body h4, +.markdown-body h5, +.markdown-body h6 { + margin-bottom: 0; + margin-top: 0; +} + +.markdown-body h1 { + font-size: 32px; +} + +.markdown-body h1, +.markdown-body h2 { + font-weight: 600; +} + +.markdown-body h2 { + font-size: 24px; +} + +.markdown-body h3 { + font-size: 20px; +} + +.markdown-body h3, +.markdown-body h4 { + font-weight: 600; +} + +.markdown-body h4 { + font-size: 16px; +} + +.markdown-body h5 { + font-size: 14px; +} + +.markdown-body h5, +.markdown-body h6 { + font-weight: 600; +} + +.markdown-body h6 { + font-size: 12px; +} + +.markdown-body p { + margin-bottom: 10px; + margin-top: 0; +} + +.markdown-body blockquote { + margin: 0; +} + +.markdown-body ol, +.markdown-body ul { + margin-bottom: 0; + margin-top: 0; + padding-left: 0; +} + +.markdown-body ol ol, +.markdown-body ul ol { + list-style-type: lower-roman; +} + +.markdown-body ol ol ol, +.markdown-body ol ul ol, +.markdown-body ul ol ol, +.markdown-body ul ul ol { + list-style-type: lower-alpha; +} + +.markdown-body dd { + margin-left: 0; +} + +.markdown-body code, +.markdown-body pre { + font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, Courier, + monospace; + font-size: 12px; +} + +.markdown-body pre { + margin-bottom: 0; + margin-top: 0; +} + +.markdown-body input::-webkit-inner-spin-button, +.markdown-body input::-webkit-outer-spin-button { + -webkit-appearance: none; + appearance: none; + margin: 0; +} + +.markdown-body .border { + border: 1px solid #e1e4e8 !important; +} + +.markdown-body .border-0 { + border: 0 !important; +} + +.markdown-body .border-bottom { + border-bottom: 1px solid #e1e4e8 !important; +} + +.markdown-body .rounded-1 { + border-radius: 3px !important; +} + +.markdown-body .bg-white { + background-color: #fff !important; +} + +.markdown-body .bg-gray-light { + background-color: #fafbfc !important; +} + +.markdown-body .text-gray-light { + color: #6a737d !important; +} + +.markdown-body .mb-0 { + margin-bottom: 0 !important; +} + +.markdown-body .my-2 { + margin-bottom: 8px !important; + margin-top: 8px !important; +} + +.markdown-body .pl-0 { + padding-left: 0 !important; +} + +.markdown-body .py-0 { + padding-bottom: 0 !important; + padding-top: 0 !important; +} + +.markdown-body .pl-1 { + padding-left: 4px !important; +} + +.markdown-body .pl-2 { + padding-left: 8px !important; +} + +.markdown-body .py-2 { + padding-bottom: 8px !important; + padding-top: 8px !important; +} + +.markdown-body .pl-3, +.markdown-body .px-3 { + padding-left: 16px !important; +} + +.markdown-body .px-3 { + padding-right: 16px !important; +} + +.markdown-body .pl-4 { + padding-left: 24px !important; +} + +.markdown-body .pl-5 { + padding-left: 32px !important; +} + +.markdown-body .pl-6 { + padding-left: 40px !important; +} + +.markdown-body .f6 { + font-size: 12px !important; +} + +.markdown-body .lh-condensed { + line-height: 1.25 !important; +} + +.markdown-body .text-bold { + font-weight: 600 !important; +} + +.markdown-body:before { + content: ''; + display: table; +} + +.markdown-body:after { + clear: both; + content: ''; + display: table; +} + +.markdown-body > :first-child { + margin-top: 0 !important; +} + +.markdown-body > :last-child { + margin-bottom: 0 !important; +} + +.markdown-body a:not([href]) { + color: inherit; + text-decoration: none; +} + +.markdown-body blockquote, +.markdown-body dl, +.markdown-body ol, +.markdown-body p, +.markdown-body pre, +.markdown-body table, +.markdown-body ul { + margin-bottom: 16px; + margin-top: 0; +} + +.markdown-body hr { + background-color: #e1e4e8; + border: 0; + height: 0.25em; + margin: 24px 0; + padding: 0; +} + +.markdown-body blockquote { + border-left: 0.25em solid #dfe2e5; + color: #6a737d; + padding: 0 1em; +} + +.markdown-body blockquote > :first-child { + margin-top: 0; +} + +.markdown-body blockquote > :last-child { + margin-bottom: 0; +} + +.markdown-body kbd { + background-color: #fafbfc; + border: 1px solid #c6cbd1; + border-bottom-color: #959da5; + border-radius: 3px; + box-shadow: inset 0 -1px 0 #959da5; + color: #444d56; + display: inline-block; + font-size: 11px; + line-height: 10px; + padding: 3px 5px; + vertical-align: middle; +} + +.markdown-body h1, +.markdown-body h2, +.markdown-body h3, +.markdown-body h4, +.markdown-body h5, +.markdown-body h6 { + font-weight: 600; + line-height: 1.25; + margin-bottom: 16px; + margin-top: 24px; +} + +.markdown-body h1 { + font-size: 2em; +} + +.markdown-body h1, +.markdown-body h2 { + padding-bottom: 0.3em; +} + +.markdown-body h2 { + border-bottom: 1px solid #eaecef; +} + +.markdown-body h2 { + font-size: 1.5em; +} + +.markdown-body h3 { + font-size: 1.25em; +} + +.markdown-body h4 { + font-size: 1em; +} + +.markdown-body h5 { + font-size: 0.875em; +} + +.markdown-body h6 { + color: #6a737d; + font-size: 0.85em; +} + +.markdown-body ol, +.markdown-body ul { + padding-left: 2em; +} + +.markdown-body ol ol, +.markdown-body ol ul, +.markdown-body ul ol, +.markdown-body ul ul { + margin-bottom: 0; + margin-top: 0; +} + +.markdown-body li { + word-wrap: break-all; +} + +.markdown-body li > p { + margin-top: 16px; +} + +.markdown-body li + li { + margin-top: 0.25em; +} + +.markdown-body dl { + padding: 0; +} + +.markdown-body dl dt { + font-size: 1em; + font-style: italic; + font-weight: 600; + margin-top: 16px; + padding: 0; +} + +.markdown-body dl dd { + margin-bottom: 16px; + padding: 0 16px; +} + +.markdown-body table { + display: block; + overflow: auto; + width: 100%; +} + +.markdown-body table th { + font-weight: 600; +} + +.markdown-body table td, +.markdown-body table th { + border: 1px solid #dfe2e5; + padding: 6px 13px; +} + +.markdown-body table tr { + background-color: #fff; + border-top: 1px solid #c6cbd1; +} + +.markdown-body table tr:nth-child(2n) { + background-color: #f6f8fa; +} + +.markdown-body img { + background-color: #fff; + box-sizing: content-box; + max-width: 100%; +} + +.markdown-body img[align='right'] { + padding-left: 20px; +} + +.markdown-body img[align='left'] { + padding-right: 20px; +} + +.markdown-body code { + background-color: rgba(27, 31, 35, 0.05); + border-radius: 3px; + font-size: 85%; + margin: 0; + padding: 0.2em 0.4em; +} + +.markdown-body pre { + word-wrap: normal; +} + +.markdown-body pre > code { + background: transparent; + border: 0; + font-size: 100%; + margin: 0; + padding: 0; + white-space: pre; + word-break: normal; + white-space: pre-wrap; +} + +.markdown-body .highlight { + margin-bottom: 16px; +} + +.markdown-body .highlight pre { + margin-bottom: 0; + word-break: normal; +} + +.markdown-body .highlight pre, +.markdown-body pre { + background-color: #f6f8fa; + border-radius: 3px; + font-size: 85%; + line-height: 1.45; + overflow: auto; + padding: 16px; +} + +.markdown-body pre code { + background-color: transparent; + border: 0; + display: inline; + line-height: inherit; + margin: 0; + max-width: auto; + overflow: visible; + padding: 0; + word-wrap: normal; +} + +.markdown-body .commit-tease-sha { + color: #444d56; + display: inline-block; + font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, Courier, + monospace; + font-size: 90%; +} + +.markdown-body .blob-wrapper { + border-bottom-left-radius: 3px; + border-bottom-right-radius: 3px; + overflow-x: auto; + overflow-y: hidden; +} + +.markdown-body .blob-wrapper-embedded { + max-height: 240px; + overflow-y: auto; +} + +.markdown-body .blob-num { + -moz-user-select: none; + -ms-user-select: none; + -webkit-user-select: none; + color: rgba(27, 31, 35, 0.3); + cursor: pointer; + font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, Courier, + monospace; + font-size: 12px; + line-height: 20px; + min-width: 50px; + padding-left: 10px; + padding-right: 10px; + text-align: right; + user-select: none; + vertical-align: top; + white-space: nowrap; + width: 1%; +} + +.markdown-body .blob-num:hover { + color: rgba(27, 31, 35, 0.6); +} + +.markdown-body .blob-num:before { + content: attr(data-line-number); +} + +.markdown-body .blob-code { + line-height: 20px; + padding-left: 10px; + padding-right: 10px; + position: relative; + vertical-align: top; +} + +.markdown-body .blob-code-inner { + color: #24292e; + font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, Courier, + monospace; + font-size: 12px; + overflow: visible; + white-space: pre; + word-wrap: normal; +} + +.markdown-body .pl-token.active, +.markdown-body .pl-token:hover { + background: #ffea7f; + cursor: pointer; +} + +.markdown-body kbd { + background-color: #fafbfc; + border: 1px solid #d1d5da; + border-bottom-color: #c6cbd1; + border-radius: 3px; + box-shadow: inset 0 -1px 0 #c6cbd1; + color: #444d56; + display: inline-block; + font: 11px SFMono-Regular, Consolas, Liberation Mono, Menlo, Courier, + monospace; + line-height: 10px; + padding: 3px 5px; + vertical-align: middle; +} + +.markdown-body :checked + .radio-label { + border-color: #0366d6; + position: relative; + z-index: 1; +} + +.markdown-body .tab-size[data-tab-size='1'] { + -moz-tab-size: 1; + tab-size: 1; +} + +.markdown-body .tab-size[data-tab-size='2'] { + -moz-tab-size: 2; + tab-size: 2; +} + +.markdown-body .tab-size[data-tab-size='3'] { + -moz-tab-size: 3; + tab-size: 3; +} + +.markdown-body .tab-size[data-tab-size='4'] { + -moz-tab-size: 4; + tab-size: 4; +} + +.markdown-body .tab-size[data-tab-size='5'] { + -moz-tab-size: 5; + tab-size: 5; +} + +.markdown-body .tab-size[data-tab-size='6'] { + -moz-tab-size: 6; + tab-size: 6; +} + +.markdown-body .tab-size[data-tab-size='7'] { + -moz-tab-size: 7; + tab-size: 7; +} + +.markdown-body .tab-size[data-tab-size='8'] { + -moz-tab-size: 8; + tab-size: 8; +} + +.markdown-body .tab-size[data-tab-size='9'] { + -moz-tab-size: 9; + tab-size: 9; +} + +.markdown-body .tab-size[data-tab-size='10'] { + -moz-tab-size: 10; + tab-size: 10; +} + +.markdown-body .tab-size[data-tab-size='11'] { + -moz-tab-size: 11; + tab-size: 11; +} + +.markdown-body .tab-size[data-tab-size='12'] { + -moz-tab-size: 12; + tab-size: 12; +} + +.markdown-body .task-list-item { + list-style-type: none; +} + +.markdown-body .task-list-item + .task-list-item { + margin-top: 3px; +} + +.markdown-body .task-list-item input { + margin: 0 0.2em 0.25em -1.6em; + vertical-align: middle; +} + +.markdown-body hr { + border-bottom-color: #eee; +} + +.markdown-body .pl-0 { + padding-left: 0 !important; +} + +.markdown-body .pl-1 { + padding-left: 4px !important; +} + +.markdown-body .pl-2 { + padding-left: 8px !important; +} + +.markdown-body .pl-3 { + padding-left: 16px !important; +} + +.markdown-body .pl-4 { + padding-left: 24px !important; +} + +.markdown-body .pl-5 { + padding-left: 32px !important; +} + +.markdown-body .pl-6 { + padding-left: 40px !important; +} + +.markdown-body .pl-7 { + padding-left: 48px !important; +} + +.markdown-body .pl-8 { + padding-left: 64px !important; +} + +.markdown-body .pl-9 { + padding-left: 80px !important; +} + +.markdown-body .pl-10 { + padding-left: 96px !important; +} + +.markdown-body .pl-11 { + padding-left: 112px !important; +} + +.markdown-body .pl-12 { + padding-left: 128px !important; +} diff --git a/pkg/server/assets/styles/src/_marker.scss b/pkg/server/assets/styles/src/_marker.scss new file mode 100644 index 00000000..75afcc10 --- /dev/null +++ b/pkg/server/assets/styles/src/_marker.scss @@ -0,0 +1,39 @@ +/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd + * + * This file is part of Dnote. + * + * Dnote is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Dnote is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Dnote. If not, see . + */ + +.marker { + display: inline-block; + padding: 0.25em 0.4em; + font-size: 75%; + font-weight: 700; + line-height: 1; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: 0.25rem; +} + +.marker-first { + color: #fff; + background-color: #007bff; +} + +.marker-info { + color: #fff; + background-color: #17a2b8; +} diff --git a/pkg/server/assets/styles/src/_note.scss b/pkg/server/assets/styles/src/_note.scss new file mode 100644 index 00000000..a903a66e --- /dev/null +++ b/pkg/server/assets/styles/src/_note.scss @@ -0,0 +1,102 @@ +.note-page { + // min-height: calc(100vh - 57px); + background: $lighter-gray; + flex-grow: 1; + flex-basis: 0; + + // .inner { + // display: flex; + // justify-content: center; + // padding-top: rem(40px); + // padding-bottom: rem(40px); + // + // @include breakpoint(md) { + // padding-top: rem(52px); + // padding-bottom: rem(52px); + // } + // } + + .header { + display: flex; + align-items: center; + justify-content: space-between; + padding: rem(12px) rem(16px); + border-bottom: 1px solid $border-color; + } + .header-left, + .header-right { + display: flex; + align-items: center; + } + + .book-icon { + vertical-align: middle; + } + + .content-wrapper { + padding: rem(12px) rem(16px); + } + + .collapsed-content { + color: $light-gray; + } + + .footer { + display: flex; + justify-content: space-between; + align-items: center; + @include font-size('small'); + padding: rem(12px) rem(16px); + } + + .ts { + color: $light-gray; + } + .ts-lead { + display: none; + @include breakpoint(md) { + display: inline; + } + } + + .match { + display: inline-block; + background: #f7f77d; + } + + .book-label { + @include font-size('medium'); + font-weight: 600; + display: inline-block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + color: $black; + + a { + color: inherit; + + &:hover { + color: inherit; + } + } + } + + // header + .header { + .book-label { + max-width: rem(200px); + margin-left: rem(12px); + + @include breakpoint(sm) { + max-width: rem(200px); + } + @include breakpoint(md) { + max-width: rem(420px); + } + @include breakpoint(lg) { + max-width: rem(600px); + } + } + } +} diff --git a/pkg/server/assets/styles/src/_reboot.scss b/pkg/server/assets/styles/src/_reboot.scss new file mode 100644 index 00000000..174f2989 --- /dev/null +++ b/pkg/server/assets/styles/src/_reboot.scss @@ -0,0 +1,367 @@ +/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd + * + * This file is part of Dnote. + * + * Dnote is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Dnote is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Dnote. If not, see . + */ + +/*! + * Bootstrap Reboot v4.3.1 (https://getbootstrap.com/) + * Copyright 2011-2019 The Bootstrap Authors + * Copyright 2011-2019 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) + */ +*, +*::before, +*::after { + box-sizing: border-box; +} + +html { + font-family: sans-serif; + line-height: 1.15; + -webkit-text-size-adjust: 100%; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} + +article, +aside, +figcaption, +figure, +footer, +header, +hgroup, +main, +nav, +section { + display: block; +} + +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, + 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', + 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: #212529; + text-align: left; + background-color: #fff; +} + +[tabindex='-1']:focus { + outline: 0 !important; +} + +hr { + box-sizing: content-box; + height: 0; + overflow: visible; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + margin-top: 0; + margin-bottom: 0.5rem; +} + +p { + margin-top: 0; + margin-bottom: 1rem; +} + +abbr[title], +abbr[data-original-title] { + text-decoration: underline; + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; + cursor: help; + border-bottom: 0; + -webkit-text-decoration-skip-ink: none; + text-decoration-skip-ink: none; +} + +address { + margin-bottom: 1rem; + font-style: normal; + line-height: inherit; +} + +ol, +ul, +dl { + margin-top: 0; + margin-bottom: 1rem; +} + +ol ol, +ul ul, +ol ul, +ul ol { + margin-bottom: 0; +} + +dt { + font-weight: 700; +} + +dd { + margin-bottom: 0.5rem; + margin-left: 0; +} + +blockquote { + margin: 0 0 1rem; +} + +b, +strong { + font-weight: bolder; +} + +small { + font-size: 80%; +} + +sub, +sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +a { + color: #007bff; + text-decoration: none; + background-color: transparent; +} + +a:hover { + color: #0056b3; + text-decoration: underline; +} + +a:not([href]):not([tabindex]) { + color: inherit; + text-decoration: none; +} + +a:not([href]):not([tabindex]):hover, +a:not([href]):not([tabindex]):focus { + color: inherit; + text-decoration: none; +} + +a:not([href]):not([tabindex]):focus { + outline: 0; +} + +pre, +code, +kbd, +samp { + font-family: SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', + 'Courier New', monospace; + font-size: 1em; +} + +pre { + margin-top: 0; + margin-bottom: 1rem; + overflow: auto; +} + +figure { + margin: 0 0 1rem; +} + +img { + vertical-align: middle; + border-style: none; +} + +svg { + overflow: hidden; + vertical-align: middle; +} + +table { + border-collapse: collapse; +} + +caption { + padding-top: 0.75rem; + padding-bottom: 0.75rem; + color: #6c757d; + text-align: left; + caption-side: bottom; +} + +th { + text-align: inherit; +} + +label { + display: inline-block; + margin-bottom: 0.5rem; +} + +button { + border-radius: 0; +} + +button:focus { + outline: 1px dotted; + outline: 5px auto -webkit-focus-ring-color; +} + +input, +button, +select, +optgroup, +textarea { + margin: 0; + font-family: inherit; + font-size: inherit; + line-height: inherit; +} + +button, +input { + overflow: visible; +} + +button, +select { + text-transform: none; +} + +select { + word-wrap: normal; +} + +button, +[type='button'], +[type='reset'], +[type='submit'] { + -webkit-appearance: button; +} + +button:not(:disabled), +[type='button']:not(:disabled), +[type='reset']:not(:disabled), +[type='submit']:not(:disabled) { + cursor: pointer; +} + +button::-moz-focus-inner, +[type='button']::-moz-focus-inner, +[type='reset']::-moz-focus-inner, +[type='submit']::-moz-focus-inner { + padding: 0; + border-style: none; +} + +input[type='radio'], +input[type='checkbox'] { + box-sizing: border-box; + padding: 0; +} + +input[type='date'], +input[type='time'], +input[type='datetime-local'], +input[type='month'] { + -webkit-appearance: listbox; +} + +textarea { + overflow: auto; + resize: vertical; +} + +fieldset { + min-width: 0; + padding: 0; + margin: 0; + border: 0; +} + +legend { + display: block; + width: 100%; + max-width: 100%; + padding: 0; + margin-bottom: 0.5rem; + font-size: 1.5rem; + line-height: inherit; + color: inherit; + white-space: normal; +} + +progress { + vertical-align: baseline; +} + +[type='number']::-webkit-inner-spin-button, +[type='number']::-webkit-outer-spin-button { + height: auto; +} + +[type='search'] { + outline-offset: -2px; + -webkit-appearance: none; +} + +[type='search']::-webkit-search-decoration { + -webkit-appearance: none; +} + +::-webkit-file-upload-button { + font: inherit; + -webkit-appearance: button; +} + +output { + display: inline-block; +} + +summary { + display: list-item; + cursor: pointer; +} + +template { + display: none; +} + +[hidden] { + display: none !important; +} +/*# sourceMappingURL=bootstrap-reboot.css.map */ diff --git a/pkg/server/assets/styles/src/_rem.scss b/pkg/server/assets/styles/src/_rem.scss new file mode 100644 index 00000000..6c9d98a9 --- /dev/null +++ b/pkg/server/assets/styles/src/_rem.scss @@ -0,0 +1,116 @@ +/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd + * + * This file is part of Dnote. + * + * Dnote is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Dnote is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Dnote. If not, see . + */ + +/* +MIT License + +Copyright (c) 2017 Pierre Burel + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +@use "sass:math"; +// assume 1 rem = 10 px +// achieved by body { font-size: 62.5%; ) +$rem-baseline: 10px !default; +$rem-fallback: false !default; +$rem-px-only: false !default; + +@function rem-separator($list, $separator: false) { + @if $separator == 'comma' or $separator == 'space' { + @return append($list, null, $separator); + } + + @if function-exists('list-separator') == true { + @return list-separator($list); + } + + // list-separator polyfill by Hugo Giraudel (https://sass-compatibility.github.io/#list_separator_function) + $test-list: (); + @each $item in $list { + $test-list: append($test-list, $item, space); + } + + @return if($test-list == $list, space, comma); +} + +@mixin rem-baseline($zoom: 100%) { + font-size: $zoom / 16px * $rem-baseline; +} + +@function rem-convert($to, $values...) { + $result: (); + $separator: rem-separator($values); + + @each $value in $values { + @if type-of($value) == 'number' and unit($value) == 'rem' and $to == 'px' { + $result: append($result, $value / 1rem * $rem-baseline, $separator); + } @else if + type-of($value) == + 'number' and + unit($value) == + 'px' and + $to == + 'rem' + { + $result: append( + $result, + math.div($value, $rem-baseline) * 1rem, + $separator + ); + } @else if type-of($value) == 'list' { + $value-separator: rem-separator($value); + $value: rem-convert($to, $value...); + $value: rem-separator($value, $value-separator); + $result: append($result, $value, $separator); + } @else { + $result: append($result, $value, $separator); + } + } + + @return if(length($result) == 1, nth($result, 1), $result); +} + +@function rem($values...) { + @if $rem-px-only { + @return rem-convert(px, $values...); + } @else { + @return rem-convert(rem, $values...); + } +} + +@mixin rem($properties, $values...) { + @if type-of($properties) == 'map' { + @each $property in map-keys($properties) { + @include rem($property, map-get($properties, $property)); + } + } @else { + @each $property in $properties { + @if $rem-fallback or $rem-px-only { + #{$property}: rem-convert(px, $values...); + } + @if not $rem-px-only { + #{$property}: rem-convert(rem, $values...); + } + } + } +} diff --git a/pkg/server/assets/styles/src/_responsive.scss b/pkg/server/assets/styles/src/_responsive.scss new file mode 100644 index 00000000..caf5bc12 --- /dev/null +++ b/pkg/server/assets/styles/src/_responsive.scss @@ -0,0 +1,62 @@ +/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd + * + * This file is part of Dnote. + * + * Dnote is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Dnote is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Dnote. If not, see . + */ + +@import './variables'; + +@mixin breakpoint($point) { + @if $point == xl { + @media (min-width: $xl-breakpoint) { + @content; + } + } @else if $point == lg { + @media (min-width: $lg-breakpoint) { + @content; + } + } @else if $point == md { + @media (min-width: $md-breakpoint) { + @content; + } + } @else if $point == sm { + @media (min-width: $sm-breakpoint) { + @content; + } + } @else if $point == smonly { + @media (min-width: $sm-breakpoint) and (max-width: $md-breakpoint - 1px) { + @content; + } + } @else if $point == smdown { + @media (max-width: $md-breakpoint - 1px) { + @content; + } + } @else if $point == mdonly { + @media (min-width: $md-breakpoint) and (max-width: $lg-breakpoint - 1px) { + @content; + } + } @else if $point == mddown { + @media (max-width: $lg-breakpoint - 1px) { + @content; + } + } +} + +// landscape is the mobile landscape mode +@mixin landscape() { + @media (max-height: 400px) { + @content; + } +} diff --git a/pkg/server/assets/styles/src/_select.scss b/pkg/server/assets/styles/src/_select.scss new file mode 100644 index 00000000..f534a64a --- /dev/null +++ b/pkg/server/assets/styles/src/_select.scss @@ -0,0 +1,463 @@ +/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd + * + * This file is part of Dnote. + * + * Dnote is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Dnote is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Dnote. If not, see . + */ + +/** + * React Select + * ============ + * Created by Jed Watson and Joss Mackison for KeystoneJS, http://www.keystonejs.com/ + * https://twitter.com/jedwatson https://twitter.com/jossmackison https://twitter.com/keystonejs + * MIT License: https://github.com/JedWatson/react-select +*/ +.Select { + position: relative; +} +.Select input::-webkit-contacts-auto-fill-button, +.Select input::-webkit-credentials-auto-fill-button { + display: none !important; +} +.Select input::-ms-clear { + display: none !important; +} +.Select input::-ms-reveal { + display: none !important; +} +.Select, +.Select div, +.Select input, +.Select span { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.Select.is-disabled .Select-arrow-zone { + cursor: default; + pointer-events: none; + opacity: 0.35; +} +.Select.is-disabled > .Select-control { + background-color: #f9f9f9; +} +.Select.is-disabled > .Select-control:hover { + box-shadow: none; +} +.Select.is-open > .Select-control { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; + background: #fff; +} +.Select.is-open > .Select-control .Select-arrow { + top: -2px; + border-color: transparent transparent #999; + border-width: 0 5px 5px; +} +.Select.is-searchable.is-open > .Select-control { + cursor: text; +} +.Select.is-searchable.is-focused:not(.is-open) > .Select-control { + cursor: text; +} +.Select.is-focused > .Select-control { + background: #fff; +} +.Select.is-focused > .Select-control { + border-top-color: #b6c9e9; + border-bottom-color: #b6c9e9; +} +.Select.is-focused:not(.is-open) > .Select-control { + background: #fff; +} +.Select.has-value.is-clearable.Select--single > .Select-control .Select-value { + padding-right: 42px; +} +.Select.has-value.Select--single > .Select-control .Select-value .Select-value-label, +.Select.has-value.is-pseudo-focused.Select--single + > .Select-control + .Select-value + .Select-value-label { + color: #333; +} +.Select.has-value.Select--single > .Select-control .Select-value a.Select-value-label, +.Select.has-value.is-pseudo-focused.Select--single + > .Select-control + .Select-value + a.Select-value-label { + cursor: pointer; + text-decoration: none; +} +.Select.has-value.Select--single > .Select-control .Select-value a.Select-value-label:hover, +.Select.has-value.is-pseudo-focused.Select--single + > .Select-control + .Select-value + a.Select-value-label:hover, +.Select.has-value.Select--single > .Select-control .Select-value a.Select-value-label:focus, +.Select.has-value.is-pseudo-focused.Select--single + > .Select-control + .Select-value + a.Select-value-label:focus { + color: #007eff; + outline: none; + text-decoration: underline; +} +.Select.has-value.Select--single > .Select-control .Select-value a.Select-value-label:focus, +.Select.has-value.is-pseudo-focused.Select--single + > .Select-control + .Select-value + a.Select-value-label:focus { + background: #fff; +} +.Select.has-value.is-pseudo-focused .Select-input { + opacity: 0; +} +.Select.is-open .Select-arrow, +.Select .Select-arrow-zone:hover > .Select-arrow { + border-top-color: #666; +} +.Select.Select--rtl { + direction: rtl; + text-align: right; +} +.Select-control { + background-color: #fff; + color: #333; + cursor: default; + display: table; + border-top: 1px solid #e2e2e2; + border-bottom: 1px solid #e2e2e2; + height: 36px; + outline: none; + overflow: hidden; + position: relative; + width: 100%; +} +.Select-control:hover { + // box-shadow: inset 0px 0px 3px 2px rgba(0, 0, 0, 0.03); +} +.Select-control .Select-input:focus { + outline: none; + background: #fff; +} +.Select-placeholder, +.Select--single > .Select-control .Select-value { + bottom: 0; + font-weight: 300; + color: #c3c3c3; + left: 0; + line-height: 34px; + // padding-left: 10px; + padding-left: 16px; + padding-right: 10px; + position: absolute; + right: 0; + top: 0; + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.Select-input { + height: 34px; + padding-left: 10px; + padding-right: 10px; + vertical-align: middle; +} +.Select-input > input { + width: 100%; + background: none transparent; + border: 0 none; + box-shadow: none; + cursor: default; + display: inline-block; + font-family: inherit; + font-size: inherit; + margin: 0; + outline: none; + line-height: 17px; + /* For IE 8 compatibility */ + padding: 8px 0 12px; + /* For IE 8 compatibility */ + -webkit-appearance: none; +} +.is-focused .Select-input > input { + cursor: text; +} +.has-value.is-pseudo-focused .Select-input { + opacity: 0; +} +.Select-control:not(.is-searchable) > .Select-input { + outline: none; +} +.Select-loading-zone { + cursor: pointer; + display: table-cell; + position: relative; + text-align: center; + vertical-align: middle; + width: 16px; +} +.Select-loading { + -webkit-animation: Select-animation-spin 400ms infinite linear; + -o-animation: Select-animation-spin 400ms infinite linear; + animation: Select-animation-spin 400ms infinite linear; + width: 16px; + height: 16px; + box-sizing: border-box; + border-radius: 50%; + border: 2px solid #ccc; + border-right-color: #333; + display: inline-block; + position: relative; + vertical-align: middle; +} +.Select-clear-zone { + -webkit-animation: Select-animation-fadeIn 200ms; + -o-animation: Select-animation-fadeIn 200ms; + animation: Select-animation-fadeIn 200ms; + color: #999; + cursor: pointer; + display: table-cell; + position: relative; + text-align: center; + vertical-align: middle; + width: 17px; +} +.Select-clear-zone:hover { + color: #d0021b; +} +.Select-clear { + display: inline-block; + font-size: 18px; + line-height: 1; +} +.Select--multi .Select-clear-zone { + width: 17px; +} +.Select-arrow-zone { + cursor: pointer; + display: table-cell; + position: relative; + text-align: center; + vertical-align: middle; + width: 25px; + padding-right: 5px; +} +.Select--rtl .Select-arrow-zone { + padding-right: 0; + padding-left: 5px; +} +.Select-arrow { + border-color: #999 transparent transparent; + border-style: solid; + border-width: 5px 5px 2.5px; + display: inline-block; + height: 0; + width: 0; + position: relative; +} +.Select-control > *:last-child { + padding-right: 5px; +} +.Select--multi .Select-multi-value-wrapper { + display: inline-block; +} +.Select .Select-aria-only { + position: absolute; + display: inline-block; + height: 1px; + width: 1px; + margin: -1px; + clip: rect(0, 0, 0, 0); + overflow: hidden; + float: left; +} +@-webkit-keyframes Select-animation-fadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } +} +@keyframes Select-animation-fadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } +} +.Select-menu-outer { + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; + background-color: #fff; + border: 1px solid #ccc; + border-top-color: #e6e6e6; + box-shadow: 0 1px 0 rgba(0, 0, 0, 0.06); + box-sizing: border-box; + margin-top: -1px; + max-height: 200px; + position: absolute; + left: 0; + top: 100%; + width: 100%; + z-index: 1; + -webkit-overflow-scrolling: touch; +} +.Select.is-open > .Select-menu-outer { + border-top-color: #b6c9e9; +} +.Select-menu { + max-height: 150px; + overflow-y: auto; +} +.Select-option { + box-sizing: border-box; + background-color: #fff; + color: #666666; + cursor: pointer; + display: block; + padding: 8px 10px; +} +.Select-option:last-child { + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +.Select-option.is-selected { + background-color: #f5faff; + /* Fallback color for IE 8 */ + background-color: rgba(0, 126, 255, 0.04); + color: #333; +} +.Select-option.is-focused { + background-color: #ebf5ff; + /* Fallback color for IE 8 */ + background-color: rgba(0, 126, 255, 0.08); + color: #333; +} +.Select-option.is-disabled { + color: #cccccc; + cursor: default; +} +.Select-noresults { + box-sizing: border-box; + color: #999999; + cursor: default; + display: block; + padding: 8px 10px; +} +.Select--multi .Select-input { + vertical-align: middle; + margin-left: 10px; + padding: 0; +} +.Select--multi.Select--rtl .Select-input { + margin-left: 0; + margin-right: 10px; +} +.Select--multi.has-value .Select-input { + margin-left: 5px; +} +.Select--multi .Select-value { + background-color: #ebf5ff; + /* Fallback color for IE 8 */ + background-color: rgba(0, 126, 255, 0.08); + border-radius: 2px; + border: 1px solid #c2e0ff; + /* Fallback color for IE 8 */ + border: 1px solid rgba(0, 126, 255, 0.24); + color: #007eff; + display: inline-block; + font-size: 0.9em; + line-height: 1.4; + margin-left: 5px; + margin-top: 5px; + vertical-align: top; +} +.Select--multi .Select-value-icon, +.Select--multi .Select-value-label { + display: inline-block; + vertical-align: middle; +} +.Select--multi .Select-value-label { + border-bottom-right-radius: 2px; + border-top-right-radius: 2px; + cursor: default; + padding: 2px 5px; +} +.Select--multi a.Select-value-label { + color: #007eff; + cursor: pointer; + text-decoration: none; +} +.Select--multi a.Select-value-label:hover { + text-decoration: underline; +} +.Select--multi .Select-value-icon { + cursor: pointer; + border-bottom-left-radius: 2px; + border-top-left-radius: 2px; + border-right: 1px solid #c2e0ff; + /* Fallback color for IE 8 */ + border-right: 1px solid rgba(0, 126, 255, 0.24); + padding: 1px 5px 3px; +} +.Select--multi .Select-value-icon:hover, +.Select--multi .Select-value-icon:focus { + background-color: #d8eafd; + /* Fallback color for IE 8 */ + background-color: rgba(0, 113, 230, 0.08); + color: #0071e6; +} +.Select--multi .Select-value-icon:active { + background-color: #c2e0ff; + /* Fallback color for IE 8 */ + background-color: rgba(0, 126, 255, 0.24); +} +.Select--multi.Select--rtl .Select-value { + margin-left: 0; + margin-right: 5px; +} +.Select--multi.Select--rtl .Select-value-icon { + border-right: none; + border-left: 1px solid #c2e0ff; + /* Fallback color for IE 8 */ + border-left: 1px solid rgba(0, 126, 255, 0.24); +} +.Select--multi.is-disabled .Select-value { + background-color: #fcfcfc; + border: 1px solid #e3e3e3; + color: #333; +} +.Select--multi.is-disabled .Select-value-icon { + cursor: not-allowed; + border-right: 1px solid #e3e3e3; +} +.Select--multi.is-disabled .Select-value-icon:hover, +.Select--multi.is-disabled .Select-value-icon:focus, +.Select--multi.is-disabled .Select-value-icon:active { + background-color: #fcfcfc; +} +@keyframes Select-animation-spin { + to { + transform: rotate(1turn); + } +} +@-webkit-keyframes Select-animation-spin { + to { + -webkit-transform: rotate(1turn); + } +} diff --git a/pkg/server/assets/styles/src/_settings.scss b/pkg/server/assets/styles/src/_settings.scss new file mode 100644 index 00000000..81c2ac43 --- /dev/null +++ b/pkg/server/assets/styles/src/_settings.scss @@ -0,0 +1,147 @@ +@import './theme'; +@import './font'; + +.settings-page { + .sidebar { + box-shadow: 0 1px 5px rgba(0, 0, 0, 0.2); + background: white; + margin-bottom: rem(20px); + margin-top: rem(20px); + + @include breakpoint(lg) { + margin-bottom: 0; + margin-top: 0; + } + } + + .sidebar-item { + display: block; + padding: rem(12px) rem(16px); + border-left: 4px solid transparent; + @include font-size('regular'); + + &:hover { + text-decoration: none; + background: $light; + } + + &.active { + font-weight: 600; + border-left-color: $first; + } + } + + .setting-section-wrapper { + .header { + @include breakpoint(lg) { + display: none; + } + } + + .setting-section { + margin-top: rem(24px); + background: white; + box-shadow: 0 0 8px rgba(0, 0, 0, 0.14); + + &:first-child { + margin-top: 0; + } + } + + .section-heading { + @include font-size('regular'); + font-weight: 600; + padding-bottom: rem(4px); + background: $light; + padding: rem(16px) rem(20px); + } + .section-content { + margin-top: rem(20px); + } + + .actions { + margin-top: rem(18px); + text-align: right; + } + } + + .setting-row { + padding: rem(16px) rem(20px); + + &:not(:last-child) { + border-bottom: 1px solid $border-color; + } + + .setting-row-summary { + display: flex; + flex-direction: column; + // align-items: flex-start; + + @include breakpoint(md) { + flex-direction: row; + justify-content: space-between; + align-items: center; + } + } + + .setting-row-main { + padding-top: rem(24px); + } + + .setting-name { + font-weight: 400; + @include font-size('regular'); + margin-bottom: 0; + } + .setting-desc { + margin-bottom: 0; + @include font-size('small'); + color: $gray; + } + .setting-action { + display: flex; + flex-direction: column; + + @include breakpoint(md) { + flex-direction: row; + } + } + + .setting-right { + display: flex; + word-break: break-all; + justify-content: space-between; + align-items: center; + margin-top: rem(4px); + + @include breakpoint(md) { + flex-direction: row; + align-items: center; + margin-top: 0; + } + } + + .setting-edit { + color: $link; + padding: 0; + + &:hover { + color: $link-hover; + } + @include breakpoint(md) { + margin-left: rem(16px); + } + } + + .input-row { + & ~ .input-row, + .input-row { + margin-top: rem(12px); + } + } + } + + .email-verification-form { + margin-left: rem(12px); + } +} diff --git a/pkg/server/assets/styles/src/_shared.scss b/pkg/server/assets/styles/src/_shared.scss new file mode 100644 index 00000000..773706bb --- /dev/null +++ b/pkg/server/assets/styles/src/_shared.scss @@ -0,0 +1,241 @@ +/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd + * + * This file is part of Dnote. + * + * Dnote is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Dnote is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Dnote. If not, see . + */ + +@import './font'; +@import './responsive'; + +@keyframes holderPulse { + 0% { + opacity: 0.4; + } + 50% { + opacity: 1; + } + 100% { + opacity: 0.4; + } +} + +// placeholder frames +.holder { + animation: holderPulse 800ms infinite; + background: #f4f4f4; + + &.holder-dark { + background: #e6e6e6; + } +} + +input[type='text']:disabled, +input[type='email']:disabled, +input[type='number']:disabled, +input[type='password']:disabled, +textarea:disabled { + background-color: $lighter-gray; + cursor: not-allowed; +} + +.list-unstyled { + list-style: none; + padding-left: 0; + margin-bottom: 0; +} + +.sr-only { + display: none; +} + +.scrollbar-measure { + position: absolute; + top: -9999px; + width: 50px; + height: 50px; + overflow: scroll; +} + +button { + img, + svg { + display: block; + } +} + +.text-input { + border: 1px solid $border-color; + padding: rem(8px) rem(12px); + position: relative; + border-radius: rem(4px); + display: block; + + &::placeholder { + color: $gray; + } + &:focus { + border-color: $light-blue; + box-shadow: inset 0 1px 2px rgba(24, 31, 35, 0.075), + 0 0 0 0.2em rgba(4, 100, 210, 0.3); + outline: none; + } +} + +.text-input-small { + padding: rem(4px) rem(12px); +} + +.text-input-medium { + padding: rem(8px) rem(12px); +} + +.text-input-stretch { + width: 100%; +} + +.label-full { + width: 100%; +} + +a { + color: $link; + + &:hover { + color: $link-hover; + } +} + +// normalize +h1, +h2, +h3, +h4, +h5, +h6 { + margin-bottom: 0; +} + +// grid +.container.mobile-fw { + @include breakpoint(mddown) { + max-width: 100%; + } +} +.container.mobile-nopadding { + @include breakpoint(mddown) { + padding-left: 0; + padding-right: 0; + + .row { + margin-left: 0; + margin-right: 0; + } + [class*='col-'] { + // Apply to all column(s) inside the row + padding-left: 0; + padding-right: 0; + } + } +} +html body { + overflow-y: scroll; +} + +.page { + padding-top: rem(20px); + padding-bottom: rem(20px); + + &.page-mobile-full { + padding-top: 0; + padding-bottom: 0; + + @include breakpoint(lg) { + padding-top: rem(32px); + padding-bottom: rem(32px); + } + } +} + +.page-header { + margin-top: rem(20px); + + &.page-header-full { + margin-bottom: rem(20px); + } + + @include breakpoint(lg) { + // padding: 0; + margin-bottom: rem(20px); + margin-top: 0; + } +} + +.form-select { + appearance: none; + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAUCAYAAACEYr13AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAACeSURBVHgBzZPBCYQwFERn2Qa2BEuwhJSyHawdrB1oB1qCV6uwhHj0qBXoBPwQRONXEXzwLoEXcCTAzfxogpPEdJyNcZCIWu8CO5+p8WOxoR9NnK3EYrEX/wOxuDmqUcSikejlXfCFfqie5ngE/ie4cVS/ibS0XB4anBhxSaKIU+xQBmLV8m6HZiW2OECEi4/JoXrO78AFHR1oTSvcxQTq7lVcue6CCAAAAABJRU5ErkJggg=='); + background-color: #fff; + background-repeat: no-repeat; + background-position: right 8px center; + background-size: 8px 10px; + border: 1px solid $border-color; + min-height: 34px; + padding: 6px 8px; + padding-right: 24px; + outline: none; + vertical-align: middle; + border-radius: 4px; + box-shadow: inset 0 1px 2px rgba(32, 36, 41, 0.08); + + &:focus { + border-color: #2188ff; + outline: none; + box-shadow: inset 0 1px 2px rgba(32, 36, 41, 0.08), + 0 0 0 2px rgba(3, 102, 214, 0.3); + } + &:disabled, + &.form-select-disabled { + background-image: url('data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAABAAAAAUCAYAAACEYr13AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAEKSURBVHgBzVTNDYIwFC4NB46OwAi4gY7gETgoE6gTGCcwTgAJ4efGCLCBjMAIXrmA3yOhQazQhJj4JQ0v7fte3/e1hbFfIk3TYxzHp6kc7dtCFEUW5/xBcdM0a9d1S1kel00mSWKCnIkkxDSnXADIMYYEU9O0zPf91WwB6L6NyB3atrUMw7hNFkCbFyROmXYYmypMDMNwo+t6ztSwtW27oEAXrXBuwu2rCht+WPgU7C8gPCBzYOBKhQS5FTwIKBYeQFeJoWyiKNYH5Co6OCuQr/0JdBuPVyElQCd7GRMb3B3HebsHHzexrmvyQvZwqjFZWsDzvCc62BFhSGYD3UMsfs6ToKOd+6EsxgtrtWLW4gUN3AAAAABJRU5ErkJggg=='); + background-color: $lighter-gray; + } +} + +.input-label { + // width: 100%; + width: auto; + font-weight: 600; + margin-bottom: rem(4px); + @include font-size('small'); +} + +.page-heading { + @include font-size('x-large'); +} + +.dropdown-caret { + display: inline-block; + vertical-align: middle; + border-top-width: 4px; + border-top-style: solid; + border-right: 4px solid transparent; + border-bottom: 0 solid transparent; + border-left: 4px solid transparent; + margin-left: rem(8px); +} + +.divider { + height: 0; + overflow: hidden; + border-top: 1px solid #e9ecef; +} diff --git a/pkg/server/assets/styles/src/_theme.scss b/pkg/server/assets/styles/src/_theme.scss new file mode 100644 index 00000000..a3e09996 --- /dev/null +++ b/pkg/server/assets/styles/src/_theme.scss @@ -0,0 +1,47 @@ +/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd + * + * This file is part of Dnote. + * + * Dnote is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Dnote is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Dnote. If not, see . + */ + +// basic colors +$black: #2a2a2a; +$white: #ffffff; +$light: #f7f9fa; +$gray: #686868; +$light-gray: #8c8c8c; +$lighter-gray: #f3f3f3; +$dark-gray: #637283; + +// primary colors +$first: #072a40; +$second: #e7e7e7; +$third: #0a4b73; + +// functional colors +$border-color: #d8d8d8; +$border-color-light: $lighter-gray; + +$link: #6f53c0; +$link-hover: darken($link, 5%); + +$danger-text: #cb2431; +$danger-background: #f8d7da; + +$blue: #0668d7; +$light-blue: #ecf4ff; +$green: #28a755; + +$active: #49abfd; diff --git a/pkg/server/api/health.go b/pkg/server/assets/styles/src/_variables.scss similarity index 76% rename from pkg/server/api/health.go rename to pkg/server/assets/styles/src/_variables.scss index 3480b796..808e20f7 100644 --- a/pkg/server/api/health.go +++ b/pkg/server/assets/styles/src/_variables.scss @@ -16,13 +16,16 @@ * along with Dnote. If not, see . */ -package api +$header-height: 60px; +$footer-height: 56px; -import ( - "net/http" -) +// breakpoints +$xl-breakpoint: 1441px; +$lg-breakpoint: 992px; +$md-breakpoint: 576px; +$sm-breakpoint: 321px; -func (a *API) checkHealth(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - w.Write([]byte("ok")) +:export { + mdBreakpoint: $md-breakpoint; + smBreakpoint: $sm-breakpoint; } diff --git a/pkg/server/assets/styles/src/main.scss b/pkg/server/assets/styles/src/main.scss new file mode 100644 index 00000000..bf1af413 --- /dev/null +++ b/pkg/server/assets/styles/src/main.scss @@ -0,0 +1,144 @@ +/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd + * + * This file is part of Dnote. + * + * Dnote is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Dnote is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Dnote. If not, see . + */ + +@import './reboot'; +@import './grid'; +@import './bootstrap'; +@import './buttons'; +@import './responsive'; +@import './select'; +@import './shared'; +@import './marker'; +@import './rem'; +@import './markdown'; +@import './hljs'; + +@import './login'; +@import './home'; +@import './note'; +@import './books'; +@import './settings'; +@import './header'; +@import './global'; + +html { + font-size: 62.5%; /* 1.0 rem = 10px */ +} + +html body { + margin: 0; + font-size: 1.6rem; +} + +img { + max-width: 100%; +} + +// input[type='email'], +// input[type='password'], +// input[type='text'] { +// &::placeholder { +// color: #aaa; +// } +// } + +.main-content { + padding-top: 24px; +} + +.no-scroll { + overflow: hidden; + + // prevent ios safari from scrolling + // but it causes page to scroll to top on modal open + // position: fixed; + // left: 0; + // right: 0; + // top: 0; + // bottom: 0; +} + +.container.mobile-nopadding { + @include breakpoint(mdonly) { + max-width: 100%; + } + + @include breakpoint(mddown) { + padding-left: 0; + padding-right: 0; + + .row { + margin-left: 0; + margin-right: 0; + } + [class*='col-'] { + // Apply to all column(s) inside the row + padding-left: 0; + padding-right: 0; + } + } +} + +// START: override bootstrap +.form-control { + font-size: 1.6rem; +} +.dropdown { + position: inherit; +} +// END: override bootstrap +// START: bootstrap related +.input-group { + input ~ button { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } +} +// END: bootstrap related + +// .twitter-follow-btn { +// position: fixed; +// bottom: 0px; +// right: 10px; +// } + +.page { + //padding: 35px 0; + //min-height: calc(100vh - 57px); + //min-height: 100vh; + // padding-top: rem(48px); +} +.page-bgdark { + background: #ececec; +} + +// Measure scrollbar width for padding body during modal show/hide +.modal-scrollbar-measure { + position: absolute; + top: -9999px; + width: 50px; + height: 50px; + overflow: scroll; +} + +.input { + border-radius: rem(4px); + background-clip: padding-box; + border: 1px solid #ced4da; + transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; +} diff --git a/pkg/server/buildinfo/info.go b/pkg/server/buildinfo/info.go new file mode 100644 index 00000000..0e7d201f --- /dev/null +++ b/pkg/server/buildinfo/info.go @@ -0,0 +1,15 @@ +package buildinfo + +var ( + // Version is the server version + Version = "master" + // CSSFiles is the css files + CSSFiles = "" + // JSFiles is the js files + JSFiles = "" + // RootURL is the root url + RootURL = "/" + // Standalone reprsents whether the build is for on-premises. It is a string + // rather than a boolean, so that it can be overridden during compile time. + Standalone = "false" +) diff --git a/pkg/server/config/config.go b/pkg/server/config/config.go index c3e5eaaa..a5d2cc3b 100644 --- a/pkg/server/config/config.go +++ b/pkg/server/config/config.go @@ -25,6 +25,11 @@ import ( "os" ) +const ( + // AppEnvProduction represents an app environment for production. + AppEnvProduction string = "PRODUCTION" +) + var ( // ErrDBMissingHost is an error for an incomplete configuration missing the host ErrDBMissingHost = errors.New("DB Host is empty") @@ -92,11 +97,25 @@ func loadDBConfig() PostgresConfig { // Config is an application configuration type Config struct { + AppEnv string WebURL string OnPremise bool DisableRegistration bool Port string DB PostgresConfig + PageTemplateDir string + StaticDir string + AssetBaseURL string +} + +func getAppEnv() string { + // DEPRECATED + goEnv := os.Getenv("GO_ENV") + if goEnv != "" { + return goEnv + } + + return os.Getenv("APP_ENV") } // Load constructs and returns a new config based on the environment variables. @@ -107,11 +126,13 @@ func Load() Config { } c := Config{ + AppEnv: getAppEnv(), WebURL: os.Getenv("WebURL"), Port: port, OnPremise: readBoolEnv("OnPremise"), DisableRegistration: readBoolEnv("DisableRegistration"), DB: loadDBConfig(), + AssetBaseURL: "", } if err := validate(c); err != nil { @@ -126,6 +147,26 @@ func (c *Config) SetOnPremise(val bool) { c.OnPremise = val } +// SetPageTemplateDir sets page template dir for the config +func (c *Config) SetPageTemplateDir(d string) { + c.PageTemplateDir = d +} + +// SetStaticDir sets static dir for the confi +func (c *Config) SetStaticDir(d string) { + c.StaticDir = d +} + +// SetAssetBaseURL sets static dir for the confi +func (c *Config) SetAssetBaseURL(d string) { + c.AssetBaseURL = d +} + +// IsProd checks if the app environment is configured to be production. +func (c Config) IsProd() bool { + return c.AppEnv == AppEnvProduction +} + func validate(c Config) error { if _, err := url.ParseRequestURI(c.WebURL); err != nil { return errors.Wrapf(ErrWebURLInvalid, "provided: '%s'", c.WebURL) diff --git a/pkg/server/consts/consts.go b/pkg/server/consts/consts.go new file mode 100644 index 00000000..8b6e83ab --- /dev/null +++ b/pkg/server/consts/consts.go @@ -0,0 +1,10 @@ +package consts + +const ( + // ContentTypeForm is the content type header for form encoded data + ContentTypeForm = "application/x-www-form-urlencoded" + // ContentTypeForm is the content type header for JSON encoded data + ContentTypeJSON = "application/json" + // ContentTypeHTML is the content type header for HTML + ContentTypeHTML = "text/html" +) diff --git a/pkg/server/context/user.go b/pkg/server/context/user.go new file mode 100644 index 00000000..81e6ba8c --- /dev/null +++ b/pkg/server/context/user.go @@ -0,0 +1,64 @@ +package context + +import ( + "context" + + "github.com/dnote/dnote/pkg/server/database" +) + +const ( + userKey privateKey = "user" + accountKey privateKey = "account" + tokenKey privateKey = "token" +) + +type privateKey string + +// WithUser creates a new context with the given user +func WithUser(ctx context.Context, user *database.User) context.Context { + return context.WithValue(ctx, userKey, user) +} + +// WithAccount creates a new context with the given account +func WithAccount(ctx context.Context, account *database.Account) context.Context { + return context.WithValue(ctx, accountKey, account) +} + +// WithToken creates a new context with the given user +func WithToken(ctx context.Context, tok *database.Token) context.Context { + return context.WithValue(ctx, tokenKey, tok) +} + +// User retrieves a user from the given context. It returns a pointer to +// a user. If the context does not contain a user, it returns nil. +func User(ctx context.Context) *database.User { + if temp := ctx.Value(userKey); temp != nil { + if user, ok := temp.(*database.User); ok { + return user + } + } + + return nil +} + +// Account retrieves an account from the given context. +func Account(ctx context.Context) *database.Account { + if temp := ctx.Value(accountKey); temp != nil { + if account, ok := temp.(*database.Account); ok { + return account + } + } + + return nil +} + +// Token retrieves a token from the given context. +func Token(ctx context.Context) *database.Token { + if temp := ctx.Value(tokenKey); temp != nil { + if tok, ok := temp.(*database.Token); ok { + return tok + } + } + + return nil +} diff --git a/pkg/server/controllers/books.go b/pkg/server/controllers/books.go new file mode 100644 index 00000000..5a937718 --- /dev/null +++ b/pkg/server/controllers/books.go @@ -0,0 +1,297 @@ +package controllers + +import ( + "fmt" + "net/http" + + "github.com/dnote/dnote/pkg/server/app" + "github.com/dnote/dnote/pkg/server/context" + "github.com/dnote/dnote/pkg/server/database" + "github.com/dnote/dnote/pkg/server/helpers" + "github.com/dnote/dnote/pkg/server/presenters" + "github.com/gorilla/mux" + "github.com/pkg/errors" +) + +// NewBooks creates a new Books controller. +// It panics if the necessary templates are not parsed. +func NewBooks(app *app.App) *Books { + return &Books{ + app: app, + } +} + +// Books is a user controller. +type Books struct { + app *app.App +} + +func (b *Books) getBooks(r *http.Request) ([]database.Book, error) { + user := context.User(r.Context()) + if user == nil { + return []database.Book{}, app.ErrLoginRequired + } + + conn := b.app.DB.Where("user_id = ? AND NOT deleted", user.ID).Order("label ASC") + + query := r.URL.Query() + name := query.Get("name") + encryptedStr := query.Get("encrypted") + + if name != "" { + part := fmt.Sprintf("%%%s%%", name) + conn = conn.Where("LOWER(label) LIKE ?", part) + } + if encryptedStr != "" { + var encrypted bool + if encryptedStr == "true" { + encrypted = true + } else { + encrypted = false + } + + conn = conn.Where("encrypted = ?", encrypted) + } + + var books []database.Book + if err := conn.Find(&books).Error; err != nil { + return []database.Book{}, nil + } + + return books, nil +} + +// V3Index gets books +func (b *Books) V3Index(w http.ResponseWriter, r *http.Request) { + result, err := b.getBooks(r) + if err != nil { + handleJSONError(w, err, "getting books") + return + } + + respondJSON(w, http.StatusOK, presenters.PresentBooks(result)) +} + +// V3Show gets a book +func (b *Books) V3Show(w http.ResponseWriter, r *http.Request) { + user := context.User(r.Context()) + if user == nil { + handleJSONError(w, app.ErrLoginRequired, "login required") + return + } + + vars := mux.Vars(r) + bookUUID := vars["bookUUID"] + + if !helpers.ValidateUUID(bookUUID) { + handleJSONError(w, app.ErrInvalidUUID, "login required") + return + } + + var book database.Book + conn := b.app.DB.Where("uuid = ? AND user_id = ?", bookUUID, user.ID).First(&book) + + if conn.RecordNotFound() { + w.WriteHeader(http.StatusNotFound) + return + } + if err := conn.Error; err != nil { + handleJSONError(w, err, "finding the book") + return + } + + respondJSON(w, http.StatusOK, presenters.PresentBook(book)) +} + +type createBookPayload struct { + Name string `schema:"name" json:"name"` +} + +func validateCreateBookPayload(p createBookPayload) error { + if p.Name == "" { + return app.ErrBookNameRequired + } + + return nil +} + +func (b *Books) create(r *http.Request) (database.Book, error) { + user := context.User(r.Context()) + if user == nil { + return database.Book{}, app.ErrLoginRequired + } + + var params createBookPayload + if err := parseRequestData(r, ¶ms); err != nil { + return database.Book{}, errors.Wrap(err, "parsing request payload") + } + + if err := validateCreateBookPayload(params); err != nil { + return database.Book{}, errors.Wrap(err, "validating payload") + } + + var bookCount int + err := b.app.DB.Model(database.Book{}). + Where("user_id = ? AND label = ?", user.ID, params.Name). + Count(&bookCount).Error + if err != nil { + return database.Book{}, errors.Wrap(err, "checking duplicate") + } + if bookCount > 0 { + return database.Book{}, app.ErrDuplicateBook + } + + book, err := b.app.CreateBook(*user, params.Name) + if err != nil { + return database.Book{}, errors.Wrap(err, "inserting a book") + } + + return book, nil +} + +// CreateBookResp is the response from create book api +type CreateBookResp struct { + Book presenters.Book `json:"book"` +} + +// V3Create creates a book +func (b *Books) V3Create(w http.ResponseWriter, r *http.Request) { + result, err := b.create(r) + if err != nil { + handleJSONError(w, err, "creating a book") + return + } + + resp := CreateBookResp{ + Book: presenters.PresentBook(result), + } + respondJSON(w, http.StatusCreated, resp) +} + +type updateBookPayload struct { + Name *string `schema:"name" json:"name"` +} + +// UpdateBookResp is the response from create book api +type UpdateBookResp struct { + Book presenters.Book `json:"book"` +} + +func (b *Books) update(r *http.Request) (database.Book, error) { + user := context.User(r.Context()) + if user == nil { + return database.Book{}, app.ErrLoginRequired + } + + vars := mux.Vars(r) + uuid := vars["bookUUID"] + + if !helpers.ValidateUUID(uuid) { + return database.Book{}, app.ErrInvalidUUID + } + + tx := b.app.DB.Begin() + + var book database.Book + if err := tx.Where("user_id = ? AND uuid = ?", user.ID, uuid).First(&book).Error; err != nil { + return database.Book{}, errors.Wrap(err, "finding book") + } + + var params updateBookPayload + if err := parseRequestData(r, ¶ms); err != nil { + return database.Book{}, errors.Wrap(err, "decoding payload") + } + + book, err := b.app.UpdateBook(tx, *user, book, params.Name) + if err != nil { + tx.Rollback() + return database.Book{}, errors.Wrap(err, "updating a book") + } + + tx.Commit() + + return book, nil +} + +// V3Update updates a book +func (b *Books) V3Update(w http.ResponseWriter, r *http.Request) { + book, err := b.update(r) + if err != nil { + handleJSONError(w, err, "updating a book") + return + } + + resp := UpdateBookResp{ + Book: presenters.PresentBook(book), + } + respondJSON(w, http.StatusOK, resp) +} + +func (b *Books) del(r *http.Request) (database.Book, error) { + user := context.User(r.Context()) + if user == nil { + return database.Book{}, app.ErrLoginRequired + } + + vars := mux.Vars(r) + uuid := vars["bookUUID"] + + if !helpers.ValidateUUID(uuid) { + return database.Book{}, app.ErrInvalidUUID + } + + tx := b.app.DB.Begin() + + var book database.Book + if err := tx.Where("user_id = ? AND uuid = ?", user.ID, uuid).First(&book).Error; err != nil { + return database.Book{}, errors.Wrap(err, "finding a book") + } + + var notes []database.Note + if err := tx.Where("book_uuid = ? AND NOT deleted", uuid).Order("usn ASC").Find(¬es).Error; err != nil { + return database.Book{}, errors.Wrap(err, "finding notes for the book") + } + + for _, note := range notes { + if _, err := b.app.DeleteNote(tx, *user, note); err != nil { + tx.Rollback() + return database.Book{}, errors.Wrap(err, "deleting a note in the book") + } + } + + book, err := b.app.DeleteBook(tx, *user, book) + if err != nil { + return database.Book{}, errors.Wrap(err, "deleting the book") + } + + tx.Commit() + + return book, nil +} + +// deleteBookResp is the response from create book api +type deleteBookResp struct { + Status int `json:"status"` + Book presenters.Book `json:"book"` +} + +// Delete updates a book +func (b *Books) V3Delete(w http.ResponseWriter, r *http.Request) { + book, err := b.del(r) + if err != nil { + handleJSONError(w, err, "creating a books") + return + } + + resp := deleteBookResp{ + Status: http.StatusOK, + Book: presenters.PresentBook(book), + } + respondJSON(w, http.StatusOK, resp) +} + +// IndexOptions is a handler for OPTIONS endpoint for notes +func (b *Books) IndexOptions(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Access-Control-Allow-Methods", "GET, POST") + w.Header().Set("Access-Control-Allow-Headers", "Authorization, Version") +} diff --git a/pkg/server/api/v3_books_test.go b/pkg/server/controllers/books_test.go similarity index 65% rename from pkg/server/api/v3_books_test.go rename to pkg/server/controllers/books_test.go index 4cd6d579..5c177f70 100644 --- a/pkg/server/api/v3_books_test.go +++ b/pkg/server/controllers/books_test.go @@ -16,17 +16,19 @@ * along with Dnote. If not, see . */ -package api +package controllers import ( "encoding/json" "fmt" + "io/ioutil" "net/http" "testing" "github.com/dnote/dnote/pkg/assert" "github.com/dnote/dnote/pkg/clock" "github.com/dnote/dnote/pkg/server/app" + "github.com/dnote/dnote/pkg/server/config" "github.com/dnote/dnote/pkg/server/database" "github.com/dnote/dnote/pkg/server/presenters" "github.com/dnote/dnote/pkg/server/testutils" @@ -34,18 +36,21 @@ import ( ) func TestGetBooks(t *testing.T) { - defer testutils.ClearData(testutils.DB) // Setup server := MustNewServer(t, &app.App{ - Clock: clock.NewMock(), + Config: config.Config{ + PageTemplateDir: "../views", + }, }) defer server.Close() user := testutils.SetupUserData() + testutils.SetupAccountData(user, "alice@test.com", "pass1234") anotherUser := testutils.SetupUserData() + testutils.SetupAccountData(anotherUser, "bob@test.com", "pass1234") b1 := database.Book{ UserID: user.ID, @@ -77,7 +82,9 @@ func TestGetBooks(t *testing.T) { testutils.MustExec(t, testutils.DB.Save(&b4), "preparing b4") // Execute - req := testutils.MakeReq(server.URL, "GET", "/v3/books", "") + endpoint := "/api/v3/books" + + req := testutils.MakeReq(server.URL, "GET", endpoint, "") res := testutils.HTTPAuthDo(t, req, user) // Test @@ -114,19 +121,21 @@ func TestGetBooks(t *testing.T) { } func TestGetBooksByName(t *testing.T) { - defer testutils.ClearData(testutils.DB) // Setup server := MustNewServer(t, &app.App{ - Clock: clock.NewMock(), + Config: config.Config{ + PageTemplateDir: "../views", + }, }) defer server.Close() user := testutils.SetupUserData() + testutils.SetupAccountData(user, "alice@test.com", "pass1234") anotherUser := testutils.SetupUserData() - req := testutils.MakeReq(server.URL, "GET", "/v3/books?name=js", "") + testutils.SetupAccountData(anotherUser, "bob@test.com", "pass1234") b1 := database.Book{ UserID: user.ID, @@ -145,6 +154,9 @@ func TestGetBooksByName(t *testing.T) { testutils.MustExec(t, testutils.DB.Save(&b3), "preparing b3") // Execute + endpoint := "/api/v3/books?name=js" + + req := testutils.MakeReq(server.URL, "GET", endpoint, "") res := testutils.HTTPAuthDo(t, req, user) // Test @@ -171,6 +183,315 @@ func TestGetBooksByName(t *testing.T) { assert.DeepEqual(t, payload, expected, "payload mismatch") } +func TestGetBook(t *testing.T) { + defer testutils.ClearData(testutils.DB) + + // Setup + server := MustNewServer(t, &app.App{ + Clock: clock.NewMock(), + Config: config.Config{ + PageTemplateDir: "../views", + }, + }) + defer server.Close() + + user := testutils.SetupUserData() + testutils.SetupAccountData(user, "alice@test.com", "pass1234") + anotherUser := testutils.SetupUserData() + testutils.SetupAccountData(anotherUser, "bob@test.com", "pass1234") + + b1 := database.Book{ + UserID: user.ID, + Label: "js", + } + testutils.MustExec(t, testutils.DB.Save(&b1), "preparing b1") + b2 := database.Book{ + UserID: user.ID, + Label: "css", + } + testutils.MustExec(t, testutils.DB.Save(&b2), "preparing b2") + b3 := database.Book{ + UserID: anotherUser.ID, + Label: "js", + } + testutils.MustExec(t, testutils.DB.Save(&b3), "preparing b3") + + // Execute + endpoint := fmt.Sprintf("/api/v3/books/%s", b1.UUID) + req := testutils.MakeReq(server.URL, "GET", endpoint, "") + res := testutils.HTTPAuthDo(t, req, user) + + // Test + assert.StatusCodeEquals(t, res, http.StatusOK, "") + + var payload presenters.Book + if err := json.NewDecoder(res.Body).Decode(&payload); err != nil { + t.Fatal(errors.Wrap(err, "decoding payload")) + } + + var b1Record database.Book + testutils.MustExec(t, testutils.DB.Where("id = ?", b1.ID).First(&b1Record), "finding b1") + + expected := presenters.Book{ + UUID: b1Record.UUID, + CreatedAt: b1Record.CreatedAt, + UpdatedAt: b1Record.UpdatedAt, + Label: b1Record.Label, + USN: b1Record.USN, + } + + assert.DeepEqual(t, payload, expected, "payload mismatch") +} + +func TestGetBookNonOwner(t *testing.T) { + defer testutils.ClearData(testutils.DB) + + // Setup + server := MustNewServer(t, &app.App{ + Clock: clock.NewMock(), + Config: config.Config{ + PageTemplateDir: "../views", + }, + }) + defer server.Close() + + user := testutils.SetupUserData() + testutils.SetupAccountData(user, "alice@test.com", "pass1234") + nonOwner := testutils.SetupUserData() + testutils.SetupAccountData(nonOwner, "bob@test.com", "pass1234") + + b1 := database.Book{ + UserID: user.ID, + Label: "js", + } + testutils.MustExec(t, testutils.DB.Save(&b1), "preparing b1") + + // Execute + endpoint := fmt.Sprintf("/api/v3/books/%s", b1.UUID) + req := testutils.MakeReq(server.URL, "GET", endpoint, "") + res := testutils.HTTPAuthDo(t, req, nonOwner) + + // Test + assert.StatusCodeEquals(t, res, http.StatusNotFound, "") + + body, err := ioutil.ReadAll(res.Body) + if err != nil { + t.Fatal(errors.Wrap(err, "reading body")) + } + assert.DeepEqual(t, string(body), "", "payload mismatch") +} + +func TestCreateBook(t *testing.T) { + t.Run("success", func(t *testing.T) { + defer testutils.ClearData(testutils.DB) + + // Setup + server := MustNewServer(t, &app.App{ + Clock: clock.NewMock(), + Config: config.Config{ + PageTemplateDir: "../views", + }, + }) + defer server.Close() + + user := testutils.SetupUserData() + testutils.SetupAccountData(user, "alice@test.com", "pass1234") + testutils.MustExec(t, testutils.DB.Model(&user).Update("max_usn", 101), "preparing user max_usn") + + req := testutils.MakeReq(server.URL, "POST", "/api/v3/books", `{"name": "js"}`) + + // Execute + res := testutils.HTTPAuthDo(t, req, user) + + // Test + assert.StatusCodeEquals(t, res, http.StatusCreated, "") + + var bookRecord database.Book + var userRecord database.User + var bookCount, noteCount int + testutils.MustExec(t, testutils.DB.Model(&database.Book{}).Count(&bookCount), "counting books") + testutils.MustExec(t, testutils.DB.Model(&database.Note{}).Count(¬eCount), "counting notes") + testutils.MustExec(t, testutils.DB.First(&bookRecord), "finding book") + testutils.MustExec(t, testutils.DB.Where("id = ?", user.ID).First(&userRecord), "finding user record") + + maxUSN := 102 + + assert.Equalf(t, bookCount, 1, "book count mismatch") + assert.Equalf(t, noteCount, 0, "note count mismatch") + + assert.NotEqual(t, bookRecord.UUID, "", "book uuid should have been generated") + assert.Equal(t, bookRecord.Label, "js", "book name mismatch") + assert.Equal(t, bookRecord.UserID, user.ID, "book user_id mismatch") + assert.Equal(t, bookRecord.USN, maxUSN, "book user_id mismatch") + assert.Equal(t, userRecord.MaxUSN, maxUSN, "user max_usn mismatch") + + var got CreateBookResp + if err := json.NewDecoder(res.Body).Decode(&got); err != nil { + t.Fatal(errors.Wrap(err, "decoding")) + } + expected := CreateBookResp{ + Book: presenters.Book{ + UUID: bookRecord.UUID, + USN: bookRecord.USN, + CreatedAt: bookRecord.CreatedAt, + UpdatedAt: bookRecord.UpdatedAt, + Label: "js", + }, + } + + assert.DeepEqual(t, got, expected, "payload mismatch") + }) + + t.Run("duplicate", func(t *testing.T) { + defer testutils.ClearData(testutils.DB) + + // Setup + server := MustNewServer(t, &app.App{ + Clock: clock.NewMock(), + Config: config.Config{ + PageTemplateDir: "../views", + }, + }) + defer server.Close() + + user := testutils.SetupUserData() + testutils.SetupAccountData(user, "alice@test.com", "pass1234") + testutils.MustExec(t, testutils.DB.Model(&user).Update("max_usn", 101), "preparing user max_usn") + + b1 := database.Book{ + UserID: user.ID, + Label: "js", + USN: 58, + } + testutils.MustExec(t, testutils.DB.Save(&b1), "preparing book data") + + // Execute + req := testutils.MakeReq(server.URL, "POST", "/api/v3/books", `{"name": "js"}`) + res := testutils.HTTPAuthDo(t, req, user) + + // Test + assert.StatusCodeEquals(t, res, http.StatusConflict, "") + + var bookRecord database.Book + var bookCount, noteCount int + var userRecord database.User + testutils.MustExec(t, testutils.DB.Model(&database.Book{}).Count(&bookCount), "counting books") + testutils.MustExec(t, testutils.DB.Model(&database.Note{}).Count(¬eCount), "counting notes") + testutils.MustExec(t, testutils.DB.First(&bookRecord), "finding book") + testutils.MustExec(t, testutils.DB.Where("id = ?", user.ID).First(&userRecord), "finding user record") + + assert.Equalf(t, bookCount, 1, "book count mismatch") + assert.Equalf(t, noteCount, 0, "note count mismatch") + + assert.Equal(t, bookRecord.Label, "js", "book name mismatch") + assert.Equal(t, bookRecord.UserID, user.ID, "book user_id mismatch") + assert.Equal(t, bookRecord.USN, b1.USN, "book usn mismatch") + assert.Equal(t, userRecord.MaxUSN, 101, "user max_usn mismatch") + }) +} + +func TestUpdateBook(t *testing.T) { + updatedLabel := "updated-label" + + b1UUID := "ead8790f-aff9-4bdf-8eec-f734ccd29202" + b2UUID := "0ecaac96-8d72-4e04-8925-5a21b79a16da" + + type payloadData struct { + Name *string `schema:"name" json:"name,omitempty"` + } + + testCases := []struct { + payload testutils.PayloadWrapper + bookUUID string + bookDeleted bool + bookLabel string + expectedBookLabel string + }{ + { + payload: testutils.PayloadWrapper{ + Data: payloadData{ + Name: &updatedLabel, + }, + }, + bookUUID: b1UUID, + bookDeleted: false, + bookLabel: "original-label", + expectedBookLabel: updatedLabel, + }, + // if a deleted book is updated, it should be un-deleted + { + payload: testutils.PayloadWrapper{ + Data: payloadData{ + Name: &updatedLabel, + }, + }, + bookUUID: b1UUID, + bookDeleted: true, + bookLabel: "", + expectedBookLabel: updatedLabel, + }, + } + + for idx, tc := range testCases { + t.Run(fmt.Sprintf("test case %d", idx), func(t *testing.T) { + defer testutils.ClearData(testutils.DB) + + // Setup + server := MustNewServer(t, &app.App{ + Clock: clock.NewMock(), + Config: config.Config{ + PageTemplateDir: "../views", + }, + }) + defer server.Close() + + user := testutils.SetupUserData() + testutils.SetupAccountData(user, "alice@test.com", "pass1234") + testutils.MustExec(t, testutils.DB.Model(&user).Update("max_usn", 101), "preparing user max_usn") + + b1 := database.Book{ + UUID: tc.bookUUID, + UserID: user.ID, + Label: tc.bookLabel, + Deleted: tc.bookDeleted, + } + testutils.MustExec(t, testutils.DB.Save(&b1), "preparing b1") + b2 := database.Book{ + UUID: b2UUID, + UserID: user.ID, + Label: "js", + } + testutils.MustExec(t, testutils.DB.Save(&b2), "preparing b2") + + // Execute + endpoint := fmt.Sprintf("/api/v3/books/%s", tc.bookUUID) + req := testutils.MakeReq(server.URL, "PATCH", endpoint, tc.payload.ToJSON(t)) + res := testutils.HTTPAuthDo(t, req, user) + + // Test + assert.StatusCodeEquals(t, res, http.StatusOK, fmt.Sprintf("status code mismatch for test case %d", idx)) + + var bookRecord database.Book + var userRecord database.User + var noteCount, bookCount int + testutils.MustExec(t, testutils.DB.Model(&database.Book{}).Count(&bookCount), "counting books") + testutils.MustExec(t, testutils.DB.Model(&database.Note{}).Count(¬eCount), "counting notes") + testutils.MustExec(t, testutils.DB.Where("id = ?", b1.ID).First(&bookRecord), "finding book") + testutils.MustExec(t, testutils.DB.Where("id = ?", user.ID).First(&userRecord), "finding user record") + + assert.Equalf(t, bookCount, 2, "book count mismatch") + assert.Equalf(t, noteCount, 0, "note count mismatch") + + assert.Equalf(t, bookRecord.UUID, tc.bookUUID, "book uuid mismatch") + assert.Equalf(t, bookRecord.Label, tc.expectedBookLabel, "book label mismatch") + assert.Equalf(t, bookRecord.USN, 102, "book usn mismatch") + assert.Equalf(t, bookRecord.Deleted, false, "book Deleted mismatch") + + assert.Equal(t, userRecord.MaxUSN, 102, fmt.Sprintf("user max_usn mismatch for test case %d", idx)) + }) + } +} + func TestDeleteBook(t *testing.T) { testCases := []struct { label string @@ -200,19 +521,22 @@ func TestDeleteBook(t *testing.T) { for _, tc := range testCases { t.Run(fmt.Sprintf("originally deleted %t", tc.deleted), func(t *testing.T) { - defer testutils.ClearData(testutils.DB) // Setup server := MustNewServer(t, &app.App{ - Clock: clock.NewMock(), + Config: config.Config{ + PageTemplateDir: "../views", + }, }) defer server.Close() user := testutils.SetupUserData() + testutils.SetupAccountData(user, "alice@test.com", "pass1234") testutils.MustExec(t, testutils.DB.Model(&user).Update("max_usn", 58), "preparing user max_usn") anotherUser := testutils.SetupUserData() + testutils.SetupAccountData(anotherUser, "bob@test.com", "pass1234") testutils.MustExec(t, testutils.DB.Model(&anotherUser).Update("max_usn", 109), "preparing another user max_usn") b1 := database.Book{ @@ -283,12 +607,10 @@ func TestDeleteBook(t *testing.T) { } testutils.MustExec(t, testutils.DB.Save(&n5), "preparing a note data") - endpoint := fmt.Sprintf("/v3/books/%s", b2.UUID) - req := testutils.MakeReq(server.URL, "DELETE", endpoint, "") - req.Header.Set("Version", "0.1.1") - req.Header.Set("Origin", "chrome-extension://iaolnfnipkoinabdbbakcmkkdignedce") - // Execute + endpoint := fmt.Sprintf("/api/v3/books/%s", b2.UUID) + + req := testutils.MakeReq(server.URL, "DELETE", endpoint, "") res := testutils.HTTPAuthDo(t, req, user) // Test @@ -349,200 +671,3 @@ func TestDeleteBook(t *testing.T) { }) } } - -func TestCreateBook(t *testing.T) { - - defer testutils.ClearData(testutils.DB) - - // Setup - server := MustNewServer(t, &app.App{ - - Clock: clock.NewMock(), - }) - defer server.Close() - - user := testutils.SetupUserData() - testutils.MustExec(t, testutils.DB.Model(&user).Update("max_usn", 101), "preparing user max_usn") - - req := testutils.MakeReq(server.URL, "POST", "/v3/books", `{"name": "js"}`) - req.Header.Set("Version", "0.1.1") - req.Header.Set("Origin", "chrome-extension://iaolnfnipkoinabdbbakcmkkdignedce") - - // Execute - res := testutils.HTTPAuthDo(t, req, user) - - // Test - assert.StatusCodeEquals(t, res, http.StatusCreated, "") - - var bookRecord database.Book - var userRecord database.User - var bookCount, noteCount int - testutils.MustExec(t, testutils.DB.Model(&database.Book{}).Count(&bookCount), "counting books") - testutils.MustExec(t, testutils.DB.Model(&database.Note{}).Count(¬eCount), "counting notes") - testutils.MustExec(t, testutils.DB.First(&bookRecord), "finding book") - testutils.MustExec(t, testutils.DB.Where("id = ?", user.ID).First(&userRecord), "finding user record") - - maxUSN := 102 - - assert.Equalf(t, bookCount, 1, "book count mismatch") - assert.Equalf(t, noteCount, 0, "note count mismatch") - - assert.NotEqual(t, bookRecord.UUID, "", "book uuid should have been generated") - assert.Equal(t, bookRecord.Label, "js", "book name mismatch") - assert.Equal(t, bookRecord.UserID, user.ID, "book user_id mismatch") - assert.Equal(t, bookRecord.USN, maxUSN, "book user_id mismatch") - assert.Equal(t, userRecord.MaxUSN, maxUSN, "user max_usn mismatch") - - var got CreateBookResp - if err := json.NewDecoder(res.Body).Decode(&got); err != nil { - t.Fatal(errors.Wrap(err, "decoding got")) - } - expected := CreateBookResp{ - Book: presenters.Book{ - UUID: bookRecord.UUID, - USN: bookRecord.USN, - CreatedAt: bookRecord.CreatedAt, - UpdatedAt: bookRecord.UpdatedAt, - Label: "js", - }, - } - - assert.DeepEqual(t, got, expected, "payload mismatch") -} - -func TestCreateBookDuplicate(t *testing.T) { - - defer testutils.ClearData(testutils.DB) - - // Setup - server := MustNewServer(t, &app.App{ - - Clock: clock.NewMock(), - }) - defer server.Close() - - user := testutils.SetupUserData() - testutils.MustExec(t, testutils.DB.Model(&user).Update("max_usn", 101), "preparing user max_usn") - - b1 := database.Book{ - UserID: user.ID, - Label: "js", - USN: 58, - } - testutils.MustExec(t, testutils.DB.Save(&b1), "preparing book data") - - // Execute - req := testutils.MakeReq(server.URL, "POST", "/v3/books", `{"name": "js"}`) - res := testutils.HTTPAuthDo(t, req, user) - - // Test - assert.StatusCodeEquals(t, res, http.StatusConflict, "") - - var bookRecord database.Book - var bookCount, noteCount int - var userRecord database.User - testutils.MustExec(t, testutils.DB.Model(&database.Book{}).Count(&bookCount), "counting books") - testutils.MustExec(t, testutils.DB.Model(&database.Note{}).Count(¬eCount), "counting notes") - testutils.MustExec(t, testutils.DB.First(&bookRecord), "finding book") - testutils.MustExec(t, testutils.DB.Where("id = ?", user.ID).First(&userRecord), "finding user record") - - assert.Equalf(t, bookCount, 1, "book count mismatch") - assert.Equalf(t, noteCount, 0, "note count mismatch") - - assert.Equal(t, bookRecord.Label, "js", "book name mismatch") - assert.Equal(t, bookRecord.UserID, user.ID, "book user_id mismatch") - assert.Equal(t, bookRecord.USN, b1.USN, "book usn mismatch") - assert.Equal(t, userRecord.MaxUSN, 101, "user max_usn mismatch") -} - -func TestUpdateBook(t *testing.T) { - updatedLabel := "updated-label" - - b1UUID := "ead8790f-aff9-4bdf-8eec-f734ccd29202" - b2UUID := "0ecaac96-8d72-4e04-8925-5a21b79a16da" - - testCases := []struct { - payload string - bookUUID string - bookDeleted bool - bookLabel string - expectedBookLabel string - }{ - { - payload: fmt.Sprintf(`{ - "name": "%s" - }`, updatedLabel), - bookUUID: b1UUID, - bookDeleted: false, - bookLabel: "original-label", - expectedBookLabel: updatedLabel, - }, - // if a deleted book is updated, it should be un-deleted - { - payload: fmt.Sprintf(`{ - "name": "%s" - }`, updatedLabel), - bookUUID: b1UUID, - bookDeleted: true, - bookLabel: "", - expectedBookLabel: updatedLabel, - }, - } - - for idx, tc := range testCases { - func() { - - defer testutils.ClearData(testutils.DB) - - // Setup - server := MustNewServer(t, &app.App{ - - Clock: clock.NewMock(), - }) - defer server.Close() - - user := testutils.SetupUserData() - testutils.MustExec(t, testutils.DB.Model(&user).Update("max_usn", 101), "preparing user max_usn") - - b1 := database.Book{ - UUID: tc.bookUUID, - UserID: user.ID, - Label: tc.bookLabel, - Deleted: tc.bookDeleted, - } - testutils.MustExec(t, testutils.DB.Save(&b1), "preparing b1") - b2 := database.Book{ - UUID: b2UUID, - UserID: user.ID, - Label: "js", - } - testutils.MustExec(t, testutils.DB.Save(&b2), "preparing b2") - - // Executdb,e - endpoint := fmt.Sprintf("/v3/books/%s", tc.bookUUID) - req := testutils.MakeReq(server.URL, "PATCH", endpoint, tc.payload) - res := testutils.HTTPAuthDo(t, req, user) - - // Test - assert.StatusCodeEquals(t, res, http.StatusOK, fmt.Sprintf("status code mismatch for test case %d", idx)) - - var bookRecord database.Book - var userRecord database.User - var noteCount, bookCount int - testutils.MustExec(t, testutils.DB.Model(&database.Book{}).Count(&bookCount), "counting books") - testutils.MustExec(t, testutils.DB.Model(&database.Note{}).Count(¬eCount), "counting notes") - testutils.MustExec(t, testutils.DB.Where("id = ?", b1.ID).First(&bookRecord), "finding book") - testutils.MustExec(t, testutils.DB.Where("id = ?", user.ID).First(&userRecord), "finding user record") - - assert.Equalf(t, bookCount, 2, "book count mismatch") - assert.Equalf(t, noteCount, 0, "note count mismatch") - - assert.Equalf(t, bookRecord.UUID, tc.bookUUID, "book uuid mismatch") - assert.Equalf(t, bookRecord.Label, tc.expectedBookLabel, "book label mismatch") - assert.Equalf(t, bookRecord.USN, 102, "book usn mismatch") - assert.Equalf(t, bookRecord.Deleted, false, "book Deleted mismatch") - - assert.Equal(t, userRecord.MaxUSN, 102, fmt.Sprintf("user max_usn mismatch for test case %d", idx)) - }() - } -} diff --git a/pkg/server/controllers/controllers.go b/pkg/server/controllers/controllers.go new file mode 100644 index 00000000..5786991b --- /dev/null +++ b/pkg/server/controllers/controllers.go @@ -0,0 +1,32 @@ +package controllers + +import ( + "github.com/dnote/dnote/pkg/server/app" + "github.com/dnote/dnote/pkg/server/log" +) + +// Controllers is a group of controllers +type Controllers struct { + Users *Users + Notes *Notes + Books *Books + Sync *Sync + Static *Static + Health *Health +} + +// New returns a new group of controllers +func New(app *app.App, baseDir string) *Controllers { + log.Info(app.Config.PageTemplateDir) + + c := Controllers{} + + c.Users = NewUsers(app, baseDir) + c.Notes = NewNotes(app) + c.Books = NewBooks(app) + c.Sync = NewSync(app) + c.Static = NewStatic(app, baseDir) + c.Health = NewHealth(app) + + return &c +} diff --git a/pkg/server/controllers/health.go b/pkg/server/controllers/health.go new file mode 100644 index 00000000..0e0608d6 --- /dev/null +++ b/pkg/server/controllers/health.go @@ -0,0 +1,23 @@ +package controllers + +import ( + "net/http" + + "github.com/dnote/dnote/pkg/server/app" +) + +// NewHealth creates a new Health controller. +// It panics if the necessary templates are not parsed. +func NewHealth(app *app.App) *Health { + return &Health{} +} + +// Health is a health controller. +type Health struct { +} + +// Index handles GET / +func (n *Health) Index(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Write([]byte("ok")) +} diff --git a/pkg/server/api/health_test.go b/pkg/server/controllers/health_test.go similarity index 80% rename from pkg/server/api/health_test.go rename to pkg/server/controllers/health_test.go index 0deadc5e..9e5b53b8 100644 --- a/pkg/server/api/health_test.go +++ b/pkg/server/controllers/health_test.go @@ -16,29 +16,30 @@ * along with Dnote. If not, see . */ -package api +package controllers import ( "net/http" "testing" "github.com/dnote/dnote/pkg/assert" - "github.com/dnote/dnote/pkg/clock" "github.com/dnote/dnote/pkg/server/app" + "github.com/dnote/dnote/pkg/server/config" "github.com/dnote/dnote/pkg/server/testutils" - "github.com/jinzhu/gorm" ) -func TestCheckHealth(t *testing.T) { - // Setup +func TestHealth(t *testing.T) { + defer testutils.ClearData(testutils.DB) + server := MustNewServer(t, &app.App{ - DB: &gorm.DB{}, - Clock: clock.NewMock(), + Config: config.Config{ + PageTemplateDir: "../views", + }, }) defer server.Close() // Execute - req := testutils.MakeReq(server.URL, "GET", "/health", "") + req := testutils.MakeReq(server.URL, "GET", "/api/health", "") res := testutils.HTTPDo(t, req) // Test diff --git a/pkg/server/controllers/helpers.go b/pkg/server/controllers/helpers.go new file mode 100644 index 00000000..7a3cb9b5 --- /dev/null +++ b/pkg/server/controllers/helpers.go @@ -0,0 +1,315 @@ +package controllers + +import ( + "encoding/json" + "net/http" + "net/url" + "strings" + "time" + + "github.com/dnote/dnote/pkg/server/app" + "github.com/dnote/dnote/pkg/server/consts" + "github.com/dnote/dnote/pkg/server/database" + "github.com/dnote/dnote/pkg/server/log" + "github.com/dnote/dnote/pkg/server/views" + "github.com/gorilla/schema" + "github.com/pkg/errors" +) + +func parseRequestData(r *http.Request, dst interface{}) error { + ct := r.Header.Get("Content-Type") + + if ct == consts.ContentTypeForm { + if err := parseForm(r, dst); err != nil { + return errors.Wrap(err, "parsing form") + } + + return nil + } + + // default to JSON + if err := parseJSON(r, dst); err != nil { + return errors.Wrap(err, "parsing JSON") + } + + return nil +} + +func parseForm(r *http.Request, dst interface{}) error { + if err := r.ParseForm(); err != nil { + return err + } + + return parseValues(r.PostForm, dst) +} + +func parseURLParams(r *http.Request, dst interface{}) error { + if err := r.ParseForm(); err != nil { + return err + } + return parseValues(r.Form, dst) +} + +func parseValues(values url.Values, dst interface{}) error { + dec := schema.NewDecoder() + + // Ignore CSRF token field + dec.IgnoreUnknownKeys(true) + + if err := dec.Decode(dst, values); err != nil { + return err + } + + return nil +} + +func parseJSON(r *http.Request, dst interface{}) error { + dec := json.NewDecoder(r.Body) + + if err := dec.Decode(dst); err != nil { + return err + } + + return nil +} + +// GetCredential extracts a session key from the request from the request header. Concretely, +// it first looks at the 'Cookie' and then the 'Authorization' header. If no credential is found, +// it returns an empty string. +func GetCredential(r *http.Request) (string, error) { + ret, err := getSessionKeyFromCookie(r) + if err != nil { + return "", errors.Wrap(err, "getting session key from cookie") + } + if ret != "" { + return ret, nil + } + + ret, err = getSessionKeyFromAuth(r) + if err != nil { + return "", errors.Wrap(err, "getting session key from Authorization header") + } + + return ret, nil +} + +// getSessionKeyFromCookie reads and returns a session key from the cookie sent by the +// request. If no session key is found, it returns an empty string +func getSessionKeyFromCookie(r *http.Request) (string, error) { + c, err := r.Cookie("id") + + if err == http.ErrNoCookie { + return "", nil + } else if err != nil { + return "", errors.Wrap(err, "reading cookie") + } + + return c.Value, nil +} + +// getSessionKeyFromAuth reads and returns a session key from the Authorization header +func getSessionKeyFromAuth(r *http.Request) (string, error) { + h := r.Header.Get("Authorization") + if h == "" { + return "", nil + } + + payload, err := parseAuthHeader(h) + if err != nil { + return "", errors.Wrap(err, "parsing the authorization header") + } + if payload.scheme != "Bearer" { + return "", errors.New("unsupported scheme") + } + + return payload.credential, nil +} + +func parseAuthHeader(h string) (authHeader, error) { + parts := strings.Split(h, " ") + + if len(parts) != 2 { + return authHeader{}, errors.New("Invalid authorization header") + } + + parsed := authHeader{ + scheme: parts[0], + credential: parts[1], + } + + return parsed, nil +} + +type authHeader struct { + scheme string + credential string +} + +const ( + sessionCookieName = "id" + sessionCookiePath = "/" +) + +func setSessionCookie(w http.ResponseWriter, key string, expires time.Time) { + cookie := http.Cookie{ + Name: sessionCookieName, + Value: key, + Expires: expires, + Path: sessionCookiePath, + HttpOnly: true, + } + http.SetCookie(w, &cookie) +} + +func unsetSessionCookie(w http.ResponseWriter) { + expires := time.Now().Add(time.Hour * -24 * 30) + cookie := http.Cookie{ + Name: sessionCookieName, + Value: "", + Expires: expires, + Path: sessionCookiePath, + HttpOnly: true, + } + + w.Header().Set("Cache-Control", "no-cache") + http.SetCookie(w, &cookie) +} + +// SessionResponse is a response containing a session information +type SessionResponse struct { + Key string `json:"key"` + ExpiresAt int64 `json:"expires_at"` +} + +func logError(err error, msg string) { + // log if internal error + // if _, ok := err.(views.PublicError); !ok { + // log.ErrorWrap(err, msg) + // } + log.ErrorWrap(err, msg) +} + +func getStatusCode(err error) int { + rootErr := errors.Cause(err) + + switch rootErr { + case app.ErrNotFound: + return http.StatusNotFound + case app.ErrLoginInvalid: + return http.StatusUnauthorized + case app.ErrDuplicateEmail, app.ErrEmailRequired, app.ErrPasswordTooShort: + return http.StatusBadRequest + case app.ErrLoginRequired: + return http.StatusUnauthorized + case app.ErrBookUUIDRequired: + return http.StatusBadRequest + case app.ErrEmptyUpdate: + return http.StatusBadRequest + case app.ErrInvalidUUID: + return http.StatusBadRequest + case app.ErrDuplicateBook: + return http.StatusConflict + case app.ErrInvalidToken: + return http.StatusBadRequest + case app.ErrPasswordResetTokenExpired: + return http.StatusGone + case app.ErrPasswordConfirmationMismatch: + return http.StatusBadRequest + case app.ErrInvalidPasswordChangeInput: + return http.StatusBadRequest + case app.ErrInvalidPassword: + return http.StatusUnauthorized + case app.ErrEmailTooLong: + return http.StatusBadRequest + case app.ErrEmailAlreadyVerified: + return http.StatusConflict + case app.ErrMissingToken: + return http.StatusBadRequest + case app.ErrExpiredToken: + return http.StatusGone + } + + return http.StatusInternalServerError +} + +// handleHTMLError writes the error to the log and sets the error message in the data. +func handleHTMLError(w http.ResponseWriter, r *http.Request, err error, msg string, v *views.View, d views.Data) { + statusCode := getStatusCode(err) + + logError(err, msg) + + d.SetAlert(err, v.AlertInBody) + v.Render(w, r, &d, statusCode) +} + +// handleJSONError logs the error and responds with the given status code with a generic status text +func handleJSONError(w http.ResponseWriter, err error, msg string) { + statusCode := getStatusCode(err) + + rootErr := errors.Cause(err) + + var respText string + if pErr, ok := rootErr.(views.PublicError); ok { + respText = pErr.Public() + } else { + respText = http.StatusText(statusCode) + } + + logError(err, msg) + http.Error(w, respText, statusCode) +} + +// respondWithSession makes a HTTP response with the session from the user with the given userID. +// It sets the HTTP-Only cookie for browser clients and also sends a JSON response for non-browser clients. +func respondWithSession(w http.ResponseWriter, statusCode int, session *database.Session) { + setSessionCookie(w, session.Key, session.ExpiresAt) + + response := SessionResponse{ + Key: session.Key, + ExpiresAt: session.ExpiresAt.Unix(), + } + + w.Header().Set("Content-Type", "application/json") + + dat, err := json.Marshal(response) + if err != nil { + handleJSONError(w, err, "encoding response") + return + } + + w.WriteHeader(statusCode) + w.Write(dat) +} + +// respondJSON encodes the given payload into a JSON format and writes it to the given response writer +func respondJSON(w http.ResponseWriter, statusCode int, payload interface{}) { + w.Header().Set("Content-Type", "application/json") + + dat, err := json.Marshal(payload) + if err != nil { + handleJSONError(w, err, "encoding response") + return + } + + w.WriteHeader(statusCode) + w.Write(dat) +} + +func getClientType(r *http.Request) string { + origin := r.Header.Get("Origin") + + if strings.HasPrefix(origin, "moz-extension://") { + return "firefox-extension" + } + + if strings.HasPrefix(origin, "chrome-extension://") { + return "chrome-extension" + } + + userAgent := r.Header.Get("User-Agent") + if strings.HasPrefix(userAgent, "Go-http-client") { + return "cli" + } + + return "web" +} diff --git a/pkg/server/job/remind/main_test.go b/pkg/server/controllers/main_test.go similarity index 97% rename from pkg/server/job/remind/main_test.go rename to pkg/server/controllers/main_test.go index d8b6b62a..286b94ee 100644 --- a/pkg/server/job/remind/main_test.go +++ b/pkg/server/controllers/main_test.go @@ -16,7 +16,7 @@ * along with Dnote. If not, see . */ -package remind +package controllers import ( "os" diff --git a/pkg/server/controllers/notes.go b/pkg/server/controllers/notes.go new file mode 100644 index 00000000..72d41817 --- /dev/null +++ b/pkg/server/controllers/notes.go @@ -0,0 +1,446 @@ +package controllers + +import ( + "math" + "net/http" + "net/url" + "sort" + "strconv" + "strings" + "time" + + "github.com/dnote/dnote/pkg/server/app" + "github.com/dnote/dnote/pkg/server/context" + "github.com/dnote/dnote/pkg/server/database" + "github.com/dnote/dnote/pkg/server/operations" + "github.com/dnote/dnote/pkg/server/presenters" + "github.com/gorilla/mux" + "github.com/pkg/errors" +) + +// NewNotes creates a new Notes controller. +// It panics if the necessary templates are not parsed. +func NewNotes(app *app.App) *Notes { + return &Notes{ + app: app, + } +} + +var notesPerPage = 30 + +// Notes is a user controller. +type Notes struct { + app *app.App +} + +// escapeSearchQuery escapes the query for full text search +func escapeSearchQuery(searchQuery string) string { + return strings.Join(strings.Fields(searchQuery), "&") +} + +func parseSearchQuery(q url.Values) string { + searchStr := q.Get("q") + + return escapeSearchQuery(searchStr) +} + +func parsePageQuery(q url.Values) (int, error) { + pageStr := q.Get("page") + if len(pageStr) == 0 { + return 1, nil + } + + p, err := strconv.Atoi(pageStr) + return p, err +} + +func parseGetNotesQuery(q url.Values) (app.GetNotesParams, error) { + yearStr := q.Get("year") + monthStr := q.Get("month") + books := q["book"] + encryptedStr := q.Get("encrypted") + pageStr := q.Get("page") + + page, err := parsePageQuery(q) + if err != nil { + return app.GetNotesParams{}, errors.Errorf("invalid page %s", pageStr) + } + if page < 1 { + return app.GetNotesParams{}, errors.Errorf("invalid page %s", pageStr) + } + + var year int + if len(yearStr) > 0 { + y, err := strconv.Atoi(yearStr) + if err != nil { + return app.GetNotesParams{}, errors.Errorf("invalid year %s", yearStr) + } + + year = y + } + + var month int + if len(monthStr) > 0 { + m, err := strconv.Atoi(monthStr) + if err != nil { + return app.GetNotesParams{}, errors.Errorf("invalid month %s", monthStr) + } + if m < 1 || m > 12 { + return app.GetNotesParams{}, errors.Errorf("invalid month %s", monthStr) + } + + month = m + } + + var encrypted bool + if strings.ToLower(encryptedStr) == "true" { + encrypted = true + } else { + encrypted = false + } + + ret := app.GetNotesParams{ + Year: year, + Month: month, + Page: page, + Search: parseSearchQuery(q), + Books: books, + Encrypted: encrypted, + PerPage: notesPerPage, + } + + return ret, nil +} + +func (n *Notes) getNotes(r *http.Request) (app.GetNotesResult, app.GetNotesParams, error) { + user := context.User(r.Context()) + if user == nil { + return app.GetNotesResult{}, app.GetNotesParams{}, app.ErrLoginRequired + } + + query := r.URL.Query() + p, err := parseGetNotesQuery(query) + if err != nil { + return app.GetNotesResult{}, app.GetNotesParams{}, errors.Wrap(err, "parsing query") + } + + res, err := n.app.GetNotes(user.ID, p) + if err != nil { + return app.GetNotesResult{}, app.GetNotesParams{}, errors.Wrap(err, "getting notes") + } + + return res, p, nil +} + +type noteGroup struct { + Year int + Month int + Data []database.Note +} + +type bucketKey struct { + year int + month time.Month +} + +func groupNotes(notes []database.Note) []noteGroup { + ret := []noteGroup{} + + buckets := map[bucketKey][]database.Note{} + + for _, note := range notes { + year := note.UpdatedAt.Year() + month := note.UpdatedAt.Month() + key := bucketKey{year, month} + + if _, ok := buckets[key]; !ok { + buckets[key] = []database.Note{} + } + + buckets[key] = append(buckets[key], note) + } + + keys := []bucketKey{} + for key := range buckets { + keys = append(keys, key) + } + + sort.Slice(keys, func(i, j int) bool { + yearI := keys[i].year + yearJ := keys[j].year + monthI := keys[i].month + monthJ := keys[j].month + + if yearI == yearJ { + return monthI < monthJ + } + + return yearI < yearJ + }) + + for _, key := range keys { + group := noteGroup{ + Year: key.year, + Month: int(key.month), + Data: buckets[key], + } + ret = append(ret, group) + } + + return ret +} + +func getMaxPage(page, total int) int { + tmp := float64(total) / float64(notesPerPage) + return int(math.Ceil(tmp)) +} + +// GetNotesResponse is a reponse by getNotesHandler +type GetNotesResponse struct { + Notes []presenters.Note `json:"notes"` + Total int `json:"total"` +} + +// V3Index is a v3 handler for getting notes +func (n *Notes) V3Index(w http.ResponseWriter, r *http.Request) { + result, _, err := n.getNotes(r) + if err != nil { + handleJSONError(w, err, "getting notes") + return + } + + respondJSON(w, http.StatusOK, GetNotesResponse{ + Notes: presenters.PresentNotes(result.Notes), + Total: result.Total, + }) +} + +func (n *Notes) getNote(r *http.Request) (database.Note, error) { + user := context.User(r.Context()) + + vars := mux.Vars(r) + noteUUID := vars["noteUUID"] + + note, ok, err := operations.GetNote(n.app.DB, noteUUID, user) + if !ok { + return database.Note{}, app.ErrNotFound + } + if err != nil { + return database.Note{}, errors.Wrap(err, "finding note") + } + + return note, nil +} + +// V3Show is api for show +func (n *Notes) V3Show(w http.ResponseWriter, r *http.Request) { + note, err := n.getNote(r) + if err != nil { + handleJSONError(w, err, "getting note") + return + } + + respondJSON(w, http.StatusOK, presenters.PresentNote(note)) +} + +type createNotePayload struct { + BookUUID string `schema:"book_uuid" json:"book_uuid"` + Content string `schema:"content" json:"content"` + AddedOn *int64 `schema:"added_on" json:"added_on"` + EditedOn *int64 `schema:"edited_on" json:"edited_on"` +} + +func validateCreateNotePayload(p createNotePayload) error { + if p.BookUUID == "" { + return app.ErrBookUUIDRequired + } + + return nil +} + +func (n *Notes) create(r *http.Request) (database.Note, error) { + user := context.User(r.Context()) + if user == nil { + return database.Note{}, app.ErrLoginRequired + } + + var params createNotePayload + if err := parseRequestData(r, ¶ms); err != nil { + return database.Note{}, errors.Wrap(err, "parsing request payload") + } + + if err := validateCreateNotePayload(params); err != nil { + return database.Note{}, err + } + + var book database.Book + if err := n.app.DB.Where("uuid = ? AND user_id = ?", params.BookUUID, user.ID).First(&book).Error; err != nil { + return database.Note{}, errors.Wrap(err, "finding book") + } + + client := getClientType(r) + note, err := n.app.CreateNote(*user, params.BookUUID, params.Content, params.AddedOn, params.EditedOn, false, client) + if err != nil { + return database.Note{}, errors.Wrap(err, "creating note") + } + + // preload associations + note.User = *user + note.Book = book + + return note, nil +} + +func (n *Notes) del(r *http.Request) (database.Note, error) { + vars := mux.Vars(r) + noteUUID := vars["noteUUID"] + + user := context.User(r.Context()) + if user == nil { + return database.Note{}, app.ErrLoginRequired + } + + var note database.Note + if err := n.app.DB.Where("uuid = ? AND user_id = ?", noteUUID, user.ID).Preload("Book").First(¬e).Error; err != nil { + return database.Note{}, errors.Wrap(err, "finding note") + } + + tx := n.app.DB.Begin() + + note, err := n.app.DeleteNote(tx, *user, note) + if err != nil { + tx.Rollback() + return database.Note{}, errors.Wrap(err, "deleting note") + } + + tx.Commit() + + return note, nil +} + +// CreateNoteResp is a response for creating a note +type CreateNoteResp struct { + Result presenters.Note `json:"result"` +} + +// V3Create creates note +func (n *Notes) V3Create(w http.ResponseWriter, r *http.Request) { + note, err := n.create(r) + if err != nil { + handleJSONError(w, err, "creating note") + return + } + + respondJSON(w, http.StatusCreated, CreateNoteResp{ + Result: presenters.PresentNote(note), + }) +} + +type DeleteNoteResp struct { + Status int `json:"status"` + Result presenters.Note `json:"result"` +} + +// V3Delete deletes note +func (n *Notes) V3Delete(w http.ResponseWriter, r *http.Request) { + note, err := n.del(r) + if err != nil { + handleJSONError(w, err, "deleting note") + return + } + + respondJSON(w, http.StatusOK, DeleteNoteResp{ + Status: http.StatusNoContent, + Result: presenters.PresentNote(note), + }) +} + +type updateNotePayload struct { + BookUUID *string `schema:"book_uuid" json:"book_uuid"` + Content *string `schema:"content" json:"content"` + Public *bool `schema:"public" json:"public"` +} + +func validateUpdateNotePayload(p updateNotePayload) error { + if p.BookUUID == nil && p.Content == nil && p.Public == nil { + return app.ErrEmptyUpdate + } + + return nil +} + +func (n *Notes) update(r *http.Request) (database.Note, error) { + vars := mux.Vars(r) + noteUUID := vars["noteUUID"] + + user := context.User(r.Context()) + if user == nil { + return database.Note{}, app.ErrLoginRequired + } + + var params updateNotePayload + err := parseRequestData(r, ¶ms) + if err != nil { + return database.Note{}, errors.Wrap(err, "decoding params") + } + + if err := validateUpdateNotePayload(params); err != nil { + return database.Note{}, err + } + + var note database.Note + if err := n.app.DB.Where("uuid = ? AND user_id = ?", noteUUID, user.ID).First(¬e).Error; err != nil { + return database.Note{}, errors.Wrap(err, "finding note") + } + + tx := n.app.DB.Begin() + + note, err = n.app.UpdateNote(tx, *user, note, &app.UpdateNoteParams{ + BookUUID: params.BookUUID, + Content: params.Content, + Public: params.Public, + }) + if err != nil { + tx.Rollback() + return database.Note{}, errors.Wrap(err, "updating note") + } + + var book database.Book + if err := tx.Where("uuid = ? AND user_id = ?", note.BookUUID, user.ID).First(&book).Error; err != nil { + tx.Rollback() + return database.Note{}, errors.Wrapf(err, "finding book %s to preload", note.BookUUID) + } + + tx.Commit() + + // preload associations + note.User = *user + note.Book = book + + return note, nil +} + +type updateNoteResp struct { + Status int `json:"status"` + Result presenters.Note `json:"result"` +} + +// V3Update updates a note +func (n *Notes) V3Update(w http.ResponseWriter, r *http.Request) { + note, err := n.update(r) + if err != nil { + handleJSONError(w, err, "updating note") + return + } + + respondJSON(w, http.StatusOK, updateNoteResp{ + Status: http.StatusOK, + Result: presenters.PresentNote(note), + }) +} + +// IndexOptions is a handler for OPTIONS endpoint for notes +func (n *Notes) IndexOptions(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Access-Control-Allow-Methods", "POST") + w.Header().Set("Access-Control-Allow-Headers", "Authorization, Version") +} diff --git a/pkg/server/controllers/notes_test.go b/pkg/server/controllers/notes_test.go new file mode 100644 index 00000000..c6fa1ffe --- /dev/null +++ b/pkg/server/controllers/notes_test.go @@ -0,0 +1,770 @@ +/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd + * + * This file is part of Dnote. + * + * Dnote is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Dnote is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Dnote. If not, see . + */ + +package controllers + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "testing" + "time" + + "github.com/dnote/dnote/pkg/assert" + "github.com/dnote/dnote/pkg/clock" + "github.com/dnote/dnote/pkg/server/app" + "github.com/dnote/dnote/pkg/server/config" + "github.com/dnote/dnote/pkg/server/database" + "github.com/dnote/dnote/pkg/server/presenters" + "github.com/dnote/dnote/pkg/server/testutils" + "github.com/pkg/errors" +) + +func getExpectedNotePayload(n database.Note, b database.Book, u database.User) presenters.Note { + return presenters.Note{ + UUID: n.UUID, + CreatedAt: n.CreatedAt, + UpdatedAt: n.UpdatedAt, + Body: n.Body, + AddedOn: n.AddedOn, + Public: n.Public, + USN: n.USN, + Book: presenters.NoteBook{ + UUID: b.UUID, + Label: b.Label, + }, + User: presenters.NoteUser{ + UUID: u.UUID, + }, + } +} + +func TestGetNotes(t *testing.T) { + defer testutils.ClearData(testutils.DB) + + // Setup + server := MustNewServer(t, &app.App{ + Clock: clock.NewMock(), + Config: config.Config{ + PageTemplateDir: "../views", + }, + }) + defer server.Close() + + user := testutils.SetupUserData() + testutils.SetupAccountData(user, "alice@test.com", "pass1234") + anotherUser := testutils.SetupUserData() + testutils.SetupAccountData(anotherUser, "bob@test.com", "pass1234") + + b1 := database.Book{ + UserID: user.ID, + Label: "js", + } + testutils.MustExec(t, testutils.DB.Save(&b1), "preparing b1") + b2 := database.Book{ + UserID: user.ID, + Label: "css", + } + testutils.MustExec(t, testutils.DB.Save(&b2), "preparing b2") + b3 := database.Book{ + UserID: anotherUser.ID, + Label: "css", + } + testutils.MustExec(t, testutils.DB.Save(&b3), "preparing b3") + + n1 := database.Note{ + UserID: user.ID, + BookUUID: b1.UUID, + Body: "n1 content", + USN: 11, + Deleted: false, + AddedOn: time.Date(2018, time.August, 10, 23, 0, 0, 0, time.UTC).UnixNano(), + } + testutils.MustExec(t, testutils.DB.Save(&n1), "preparing n1") + n2 := database.Note{ + UserID: user.ID, + BookUUID: b1.UUID, + Body: "n2 content", + USN: 14, + Deleted: false, + AddedOn: time.Date(2018, time.August, 11, 22, 0, 0, 0, time.UTC).UnixNano(), + } + testutils.MustExec(t, testutils.DB.Save(&n2), "preparing n2") + n3 := database.Note{ + UserID: user.ID, + BookUUID: b1.UUID, + Body: "n3 content", + USN: 17, + Deleted: false, + AddedOn: time.Date(2017, time.January, 10, 23, 0, 0, 0, time.UTC).UnixNano(), + } + testutils.MustExec(t, testutils.DB.Save(&n3), "preparing n3") + n4 := database.Note{ + UserID: user.ID, + BookUUID: b2.UUID, + Body: "n4 content", + USN: 18, + Deleted: false, + AddedOn: time.Date(2018, time.September, 10, 23, 0, 0, 0, time.UTC).UnixNano(), + } + testutils.MustExec(t, testutils.DB.Save(&n4), "preparing n4") + n5 := database.Note{ + UserID: anotherUser.ID, + BookUUID: b3.UUID, + Body: "n5 content", + USN: 19, + Deleted: false, + AddedOn: time.Date(2018, time.August, 10, 23, 0, 0, 0, time.UTC).UnixNano(), + } + testutils.MustExec(t, testutils.DB.Save(&n5), "preparing n5") + n6 := database.Note{ + UserID: user.ID, + BookUUID: b1.UUID, + Body: "", + USN: 11, + Deleted: true, + AddedOn: time.Date(2018, time.August, 10, 23, 0, 0, 0, time.UTC).UnixNano(), + } + testutils.MustExec(t, testutils.DB.Save(&n6), "preparing n6") + + // Execute + endpoint := "/api/v3/notes" + + req := testutils.MakeReq(server.URL, "GET", fmt.Sprintf("%s?year=2018&month=8", endpoint), "") + res := testutils.HTTPAuthDo(t, req, user) + + // Test + assert.StatusCodeEquals(t, res, http.StatusOK, "") + + var payload GetNotesResponse + if err := json.NewDecoder(res.Body).Decode(&payload); err != nil { + t.Fatal(errors.Wrap(err, "decoding payload")) + } + + var n2Record, n1Record database.Note + testutils.MustExec(t, testutils.DB.Where("uuid = ?", n2.UUID).First(&n2Record), "finding n2Record") + testutils.MustExec(t, testutils.DB.Where("uuid = ?", n1.UUID).First(&n1Record), "finding n1Record") + + expected := GetNotesResponse{ + Notes: []presenters.Note{ + getExpectedNotePayload(n2Record, b1, user), + getExpectedNotePayload(n1Record, b1, user), + }, + Total: 2, + } + + assert.DeepEqual(t, payload, expected, "payload mismatch") +} + +func TestGetNote(t *testing.T) { + defer testutils.ClearData(testutils.DB) + + // Setup + server := MustNewServer(t, &app.App{ + Clock: clock.NewMock(), + Config: config.Config{ + PageTemplateDir: "../views", + }, + }) + defer server.Close() + + user := testutils.SetupUserData() + anotherUser := testutils.SetupUserData() + + b1 := database.Book{ + UserID: user.ID, + Label: "js", + } + testutils.MustExec(t, testutils.DB.Save(&b1), "preparing b1") + + privateNote := database.Note{ + UserID: user.ID, + BookUUID: b1.UUID, + Body: "privateNote content", + Public: false, + } + testutils.MustExec(t, testutils.DB.Save(&privateNote), "preparing privateNote") + publicNote := database.Note{ + UserID: user.ID, + BookUUID: b1.UUID, + Body: "publicNote content", + Public: true, + } + testutils.MustExec(t, testutils.DB.Save(&publicNote), "preparing publicNote") + deletedNote := database.Note{ + UserID: user.ID, + BookUUID: b1.UUID, + Deleted: true, + } + testutils.MustExec(t, testutils.DB.Save(&deletedNote), "preparing deletedNote") + + getURL := func(noteUUID string) string { + return fmt.Sprintf("/api/v3/notes/%s", noteUUID) + } + + t.Run("owner accessing private note", func(t *testing.T) { + // Execute + url := getURL(publicNote.UUID) + req := testutils.MakeReq(server.URL, "GET", url, "") + res := testutils.HTTPAuthDo(t, req, user) + + // Test + assert.StatusCodeEquals(t, res, http.StatusOK, "") + + var payload presenters.Note + if err := json.NewDecoder(res.Body).Decode(&payload); err != nil { + t.Fatal(errors.Wrap(err, "decoding payload")) + } + + var n2Record database.Note + testutils.MustExec(t, testutils.DB.Where("uuid = ?", publicNote.UUID).First(&n2Record), "finding n2Record") + + expected := getExpectedNotePayload(n2Record, b1, user) + assert.DeepEqual(t, payload, expected, "payload mismatch") + }) + + t.Run("owner accessing public note", func(t *testing.T) { + // Execute + url := getURL(publicNote.UUID) + req := testutils.MakeReq(server.URL, "GET", url, "") + res := testutils.HTTPAuthDo(t, req, user) + + // Test + assert.StatusCodeEquals(t, res, http.StatusOK, "") + + var payload presenters.Note + if err := json.NewDecoder(res.Body).Decode(&payload); err != nil { + t.Fatal(errors.Wrap(err, "decoding payload")) + } + + var n2Record database.Note + testutils.MustExec(t, testutils.DB.Where("uuid = ?", publicNote.UUID).First(&n2Record), "finding n2Record") + + expected := getExpectedNotePayload(n2Record, b1, user) + assert.DeepEqual(t, payload, expected, "payload mismatch") + }) + + t.Run("non-owner accessing public note", func(t *testing.T) { + // Execute + url := getURL(publicNote.UUID) + req := testutils.MakeReq(server.URL, "GET", url, "") + res := testutils.HTTPAuthDo(t, req, anotherUser) + + // Test + assert.StatusCodeEquals(t, res, http.StatusOK, "") + + var payload presenters.Note + if err := json.NewDecoder(res.Body).Decode(&payload); err != nil { + t.Fatal(errors.Wrap(err, "decoding payload")) + } + + var n2Record database.Note + testutils.MustExec(t, testutils.DB.Where("uuid = ?", publicNote.UUID).First(&n2Record), "finding n2Record") + + expected := getExpectedNotePayload(n2Record, b1, user) + assert.DeepEqual(t, payload, expected, "payload mismatch") + }) + + t.Run("non-owner accessing private note", func(t *testing.T) { + // Execute + url := getURL(privateNote.UUID) + req := testutils.MakeReq(server.URL, "GET", url, "") + res := testutils.HTTPAuthDo(t, req, anotherUser) + + // Test + assert.StatusCodeEquals(t, res, http.StatusNotFound, "") + + body, err := ioutil.ReadAll(res.Body) + if err != nil { + t.Fatal(errors.Wrap(err, "reading body")) + } + + assert.DeepEqual(t, string(body), "not found\n", "payload mismatch") + }) + + t.Run("guest accessing public note", func(t *testing.T) { + // Execute + url := getURL(publicNote.UUID) + req := testutils.MakeReq(server.URL, "GET", url, "") + res := testutils.HTTPDo(t, req) + + // Test + assert.StatusCodeEquals(t, res, http.StatusOK, "") + + var payload presenters.Note + if err := json.NewDecoder(res.Body).Decode(&payload); err != nil { + t.Fatal(errors.Wrap(err, "decoding payload")) + } + + var n2Record database.Note + testutils.MustExec(t, testutils.DB.Where("uuid = ?", publicNote.UUID).First(&n2Record), "finding n2Record") + + expected := getExpectedNotePayload(n2Record, b1, user) + assert.DeepEqual(t, payload, expected, "payload mismatch") + }) + + t.Run("guest accessing private note", func(t *testing.T) { + // Execute + url := getURL(privateNote.UUID) + req := testutils.MakeReq(server.URL, "GET", url, "") + res := testutils.HTTPDo(t, req) + + // Test + assert.StatusCodeEquals(t, res, http.StatusNotFound, "") + + body, err := ioutil.ReadAll(res.Body) + if err != nil { + t.Fatal(errors.Wrap(err, "reading body")) + } + + assert.DeepEqual(t, string(body), "not found\n", "payload mismatch") + }) + + t.Run("nonexistent", func(t *testing.T) { + // Execute + url := getURL("somerandomstring") + req := testutils.MakeReq(server.URL, "GET", url, "") + res := testutils.HTTPAuthDo(t, req, user) + + // Test + assert.StatusCodeEquals(t, res, http.StatusNotFound, "") + + body, err := ioutil.ReadAll(res.Body) + if err != nil { + t.Fatal(errors.Wrap(err, "reading body")) + } + + assert.DeepEqual(t, string(body), "not found\n", "payload mismatch") + }) + + t.Run("deleted", func(t *testing.T) { + // Execute + url := getURL(deletedNote.UUID) + req := testutils.MakeReq(server.URL, "GET", url, "") + res := testutils.HTTPAuthDo(t, req, user) + + // Test + assert.StatusCodeEquals(t, res, http.StatusNotFound, "") + + body, err := ioutil.ReadAll(res.Body) + if err != nil { + t.Fatal(errors.Wrap(err, "reading body")) + } + + assert.DeepEqual(t, string(body), "not found\n", "payload mismatch") + }) +} + +func TestCreateNote(t *testing.T) { + defer testutils.ClearData(testutils.DB) + + // Setup + server := MustNewServer(t, &app.App{ + Clock: clock.NewMock(), + Config: config.Config{ + PageTemplateDir: "../views", + }, + }) + defer server.Close() + + user := testutils.SetupUserData() + testutils.SetupAccountData(user, "alice@test.com", "pass1234") + testutils.MustExec(t, testutils.DB.Model(&user).Update("max_usn", 101), "preparing user max_usn") + + b1 := database.Book{ + UserID: user.ID, + Label: "js", + USN: 58, + } + testutils.MustExec(t, testutils.DB.Save(&b1), "preparing b1") + + // Execute + + dat := fmt.Sprintf(`{"book_uuid": "%s", "content": "note content"}`, b1.UUID) + req := testutils.MakeReq(server.URL, "POST", "/api/v3/notes", dat) + res := testutils.HTTPAuthDo(t, req, user) + + // Test + assert.StatusCodeEquals(t, res, http.StatusCreated, "") + + var noteRecord database.Note + var bookRecord database.Book + var userRecord database.User + var bookCount, noteCount int + testutils.MustExec(t, testutils.DB.Model(&database.Book{}).Count(&bookCount), "counting books") + testutils.MustExec(t, testutils.DB.Model(&database.Note{}).Count(¬eCount), "counting notes") + testutils.MustExec(t, testutils.DB.First(¬eRecord), "finding note") + testutils.MustExec(t, testutils.DB.Where("id = ?", b1.ID).First(&bookRecord), "finding book") + testutils.MustExec(t, testutils.DB.Where("id = ?", user.ID).First(&userRecord), "finding user record") + + assert.Equalf(t, bookCount, 1, "book count mismatch") + assert.Equalf(t, noteCount, 1, "note count mismatch") + + assert.Equal(t, bookRecord.Label, b1.Label, "book name mismatch") + assert.Equal(t, bookRecord.UUID, b1.UUID, "book uuid mismatch") + assert.Equal(t, bookRecord.UserID, b1.UserID, "book user_id mismatch") + assert.Equal(t, bookRecord.USN, 58, "book usn mismatch") + + assert.NotEqual(t, noteRecord.UUID, "", "note uuid should have been generated") + assert.Equal(t, noteRecord.BookUUID, b1.UUID, "note book_uuid mismatch") + assert.Equal(t, noteRecord.Body, "note content", "note content mismatch") + assert.Equal(t, noteRecord.USN, 102, "note usn mismatch") +} + +func TestDeleteNote(t *testing.T) { + b1UUID := "37868a8e-a844-4265-9a4f-0be598084733" + + testCases := []struct { + content string + deleted bool + originalUSN int + expectedUSN int + expectedMaxUSN int + }{ + { + content: "n1 content", + deleted: false, + originalUSN: 12, + expectedUSN: 982, + expectedMaxUSN: 982, + }, + { + content: "", + deleted: true, + originalUSN: 12, + expectedUSN: 982, + expectedMaxUSN: 982, + }, + } + + for idx, tc := range testCases { + t.Run(fmt.Sprintf("test case %d", idx), func(t *testing.T) { + defer testutils.ClearData(testutils.DB) + + // Setup + server := MustNewServer(t, &app.App{ + Clock: clock.NewMock(), + Config: config.Config{ + PageTemplateDir: "../views", + }, + }) + defer server.Close() + + user := testutils.SetupUserData() + testutils.SetupAccountData(user, "alice@test.com", "pass1234") + testutils.MustExec(t, testutils.DB.Model(&user).Update("max_usn", 981), "preparing user max_usn") + + b1 := database.Book{ + UUID: b1UUID, + UserID: user.ID, + Label: "js", + } + testutils.MustExec(t, testutils.DB.Save(&b1), "preparing b1") + note := database.Note{ + UserID: user.ID, + BookUUID: b1.UUID, + Body: tc.content, + Deleted: tc.deleted, + USN: tc.originalUSN, + } + testutils.MustExec(t, testutils.DB.Save(¬e), "preparing note") + + // Execute + endpoint := fmt.Sprintf("/api/v3/notes/%s", note.UUID) + req := testutils.MakeReq(server.URL, "DELETE", endpoint, "") + res := testutils.HTTPAuthDo(t, req, user) + + // Test + assert.StatusCodeEquals(t, res, http.StatusOK, "") + + var bookRecord database.Book + var noteRecord database.Note + var userRecord database.User + var bookCount, noteCount int + testutils.MustExec(t, testutils.DB.Model(&database.Book{}).Count(&bookCount), "counting books") + testutils.MustExec(t, testutils.DB.Model(&database.Note{}).Count(¬eCount), "counting notes") + testutils.MustExec(t, testutils.DB.Where("uuid = ?", note.UUID).First(¬eRecord), "finding note") + testutils.MustExec(t, testutils.DB.Where("id = ?", b1.ID).First(&bookRecord), "finding book") + testutils.MustExec(t, testutils.DB.Where("id = ?", user.ID).First(&userRecord), "finding user record") + + assert.Equalf(t, bookCount, 1, "book count mismatch") + assert.Equalf(t, noteCount, 1, "note count mismatch") + + assert.Equal(t, noteRecord.UUID, note.UUID, "note uuid mismatch for test case") + assert.Equal(t, noteRecord.Body, "", "note content mismatch for test case") + assert.Equal(t, noteRecord.Deleted, true, "note deleted mismatch for test case") + assert.Equal(t, noteRecord.BookUUID, note.BookUUID, "note book_uuid mismatch for test case") + assert.Equal(t, noteRecord.UserID, note.UserID, "note user_id mismatch for test case") + assert.Equal(t, noteRecord.USN, tc.expectedUSN, "note usn mismatch for test case") + + assert.Equal(t, userRecord.MaxUSN, tc.expectedMaxUSN, "user max_usn mismatch for test case") + }) + } +} + +func TestUpdateNote(t *testing.T) { + updatedBody := "some updated content" + + b1UUID := "37868a8e-a844-4265-9a4f-0be598084733" + b2UUID := "8f3bd424-6aa5-4ed5-910d-e5b38ab09f8c" + + type payloadData struct { + Content *string `schema:"content" json:"content,omitempty"` + BookUUID *string `schema:"book_uuid" json:"book_uuid,omitempty"` + Public *bool `schema:"public" json:"public,omitempty"` + } + + testCases := []struct { + payload testutils.PayloadWrapper + noteUUID string + noteBookUUID string + noteBody string + notePublic bool + noteDeleted bool + expectedNoteBody string + expectedNoteBookName string + expectedNoteBookUUID string + expectedNotePublic bool + }{ + { + payload: testutils.PayloadWrapper{ + Data: payloadData{ + Content: &updatedBody, + }, + }, + noteUUID: "ab50aa32-b232-40d8-b10f-10a7f9134053", + noteBookUUID: b1UUID, + notePublic: false, + noteBody: "original content", + noteDeleted: false, + expectedNoteBookUUID: b1UUID, + expectedNoteBody: "some updated content", + expectedNoteBookName: "css", + expectedNotePublic: false, + }, + { + payload: testutils.PayloadWrapper{ + Data: payloadData{ + BookUUID: &b1UUID, + }, + }, + noteUUID: "ab50aa32-b232-40d8-b10f-10a7f9134053", + noteBookUUID: b1UUID, + notePublic: false, + noteBody: "original content", + noteDeleted: false, + expectedNoteBookUUID: b1UUID, + expectedNoteBody: "original content", + expectedNoteBookName: "css", + expectedNotePublic: false, + }, + { + payload: testutils.PayloadWrapper{ + Data: payloadData{ + BookUUID: &b2UUID, + }, + }, + noteUUID: "ab50aa32-b232-40d8-b10f-10a7f9134053", + noteBookUUID: b1UUID, + notePublic: false, + noteBody: "original content", + noteDeleted: false, + expectedNoteBookUUID: b2UUID, + expectedNoteBody: "original content", + expectedNoteBookName: "js", + expectedNotePublic: false, + }, + { + payload: testutils.PayloadWrapper{ + Data: payloadData{ + BookUUID: &b2UUID, + Content: &updatedBody, + }, + }, + noteUUID: "ab50aa32-b232-40d8-b10f-10a7f9134053", + noteBookUUID: b1UUID, + notePublic: false, + noteBody: "original content", + noteDeleted: false, + expectedNoteBookUUID: b2UUID, + expectedNoteBody: "some updated content", + expectedNoteBookName: "js", + expectedNotePublic: false, + }, + { + payload: testutils.PayloadWrapper{ + Data: payloadData{ + BookUUID: &b1UUID, + Content: &updatedBody, + }, + }, + noteUUID: "ab50aa32-b232-40d8-b10f-10a7f9134053", + noteBookUUID: b1UUID, + notePublic: false, + noteBody: "", + noteDeleted: true, + expectedNoteBookUUID: b1UUID, + expectedNoteBody: updatedBody, + expectedNoteBookName: "js", + expectedNotePublic: false, + }, + { + payload: testutils.PayloadWrapper{ + Data: payloadData{ + Public: &testutils.TrueVal, + }, + }, + noteUUID: "ab50aa32-b232-40d8-b10f-10a7f9134053", + noteBookUUID: b1UUID, + notePublic: false, + noteBody: "original content", + noteDeleted: false, + expectedNoteBookUUID: b1UUID, + expectedNoteBody: "original content", + expectedNoteBookName: "css", + expectedNotePublic: true, + }, + { + payload: testutils.PayloadWrapper{ + Data: payloadData{ + Public: &testutils.FalseVal, + }, + }, + noteUUID: "ab50aa32-b232-40d8-b10f-10a7f9134053", + noteBookUUID: b1UUID, + notePublic: true, + noteBody: "original content", + noteDeleted: false, + expectedNoteBookUUID: b1UUID, + expectedNoteBody: "original content", + expectedNoteBookName: "css", + expectedNotePublic: false, + }, + { + payload: testutils.PayloadWrapper{ + Data: payloadData{ + Content: &updatedBody, + Public: &testutils.FalseVal, + }, + }, + noteUUID: "ab50aa32-b232-40d8-b10f-10a7f9134053", + noteBookUUID: b1UUID, + notePublic: true, + noteBody: "original content", + noteDeleted: false, + expectedNoteBookUUID: b1UUID, + expectedNoteBody: updatedBody, + expectedNoteBookName: "css", + expectedNotePublic: false, + }, + { + payload: testutils.PayloadWrapper{ + Data: payloadData{ + BookUUID: &b2UUID, + Content: &updatedBody, + Public: &testutils.TrueVal, + }, + }, + noteUUID: "ab50aa32-b232-40d8-b10f-10a7f9134053", + noteBookUUID: b1UUID, + notePublic: false, + noteBody: "original content", + noteDeleted: false, + expectedNoteBookUUID: b2UUID, + expectedNoteBody: updatedBody, + expectedNoteBookName: "js", + expectedNotePublic: true, + }, + } + + for idx, tc := range testCases { + t.Run(fmt.Sprintf("test case %d", idx), func(t *testing.T) { + defer testutils.ClearData(testutils.DB) + + // Setup + server := MustNewServer(t, &app.App{ + Clock: clock.NewMock(), + Config: config.Config{ + PageTemplateDir: "../views", + }, + }) + defer server.Close() + + user := testutils.SetupUserData() + testutils.SetupAccountData(user, "alice@test.com", "pass1234") + + testutils.MustExec(t, testutils.DB.Model(&user).Update("max_usn", 101), "preparing user max_usn") + + b1 := database.Book{ + UUID: b1UUID, + UserID: user.ID, + Label: "css", + } + testutils.MustExec(t, testutils.DB.Save(&b1), "preparing b1") + b2 := database.Book{ + UUID: b2UUID, + UserID: user.ID, + Label: "js", + } + testutils.MustExec(t, testutils.DB.Save(&b2), "preparing b2") + + note := database.Note{ + UserID: user.ID, + UUID: tc.noteUUID, + BookUUID: tc.noteBookUUID, + Body: tc.noteBody, + Deleted: tc.noteDeleted, + Public: tc.notePublic, + } + testutils.MustExec(t, testutils.DB.Save(¬e), "preparing note") + + // Execute + var req *http.Request + + endpoint := fmt.Sprintf("/api/v3/notes/%s", note.UUID) + req = testutils.MakeReq(server.URL, "PATCH", endpoint, tc.payload.ToJSON(t)) + + res := testutils.HTTPAuthDo(t, req, user) + + // Test + assert.StatusCodeEquals(t, res, http.StatusOK, "status code mismatch for test case") + + var bookRecord database.Book + var noteRecord database.Note + var userRecord database.User + var noteCount, bookCount int + testutils.MustExec(t, testutils.DB.Model(&database.Book{}).Count(&bookCount), "counting books") + testutils.MustExec(t, testutils.DB.Model(&database.Note{}).Count(¬eCount), "counting notes") + testutils.MustExec(t, testutils.DB.Where("uuid = ?", note.UUID).First(¬eRecord), "finding note") + testutils.MustExec(t, testutils.DB.Where("id = ?", b1.ID).First(&bookRecord), "finding book") + testutils.MustExec(t, testutils.DB.Where("id = ?", user.ID).First(&userRecord), "finding user record") + + assert.Equalf(t, bookCount, 2, "book count mismatch") + assert.Equalf(t, noteCount, 1, "note count mismatch") + + assert.Equal(t, noteRecord.UUID, tc.noteUUID, "note uuid mismatch for test case") + assert.Equal(t, noteRecord.Body, tc.expectedNoteBody, "note content mismatch for test case") + assert.Equal(t, noteRecord.BookUUID, tc.expectedNoteBookUUID, "note book_uuid mismatch for test case") + assert.Equal(t, noteRecord.Public, tc.expectedNotePublic, "note public mismatch for test case") + assert.Equal(t, noteRecord.USN, 102, "note usn mismatch for test case") + + assert.Equal(t, userRecord.MaxUSN, 102, "user max_usn mismatch for test case") + }) + } +} diff --git a/pkg/server/controllers/routes.go b/pkg/server/controllers/routes.go new file mode 100644 index 00000000..af76bf76 --- /dev/null +++ b/pkg/server/controllers/routes.go @@ -0,0 +1,124 @@ +package controllers + +import ( + "net/http" + + "github.com/dnote/dnote/pkg/server/app" + mw "github.com/dnote/dnote/pkg/server/middleware" + "github.com/gorilla/mux" + "github.com/pkg/errors" +) + +// Route represents a single route +type Route struct { + Method string + Pattern string + Handler http.HandlerFunc + RateLimit bool +} + +// RouteConfig is the configuration for routes +type RouteConfig struct { + Controllers *Controllers + WebRoutes []Route + APIRoutes []Route +} + +// NewWebRoutes returns a new web routes +func NewWebRoutes(a *app.App, c *Controllers) []Route { + redirectGuest := &mw.AuthParams{RedirectGuestsToLogin: true} + + ret := []Route{ + {"GET", "/", mw.Auth(a, c.Users.Settings, redirectGuest), true}, + {"GET", "/about", mw.Auth(a, c.Users.About, redirectGuest), true}, + {"GET", "/login", mw.GuestOnly(a, c.Users.NewLogin), true}, + {"POST", "/login", mw.GuestOnly(a, c.Users.Login), true}, + {"POST", "/logout", c.Users.Logout, true}, + + {"GET", "/password-reset", c.Users.PasswordResetView.ServeHTTP, true}, + {"PATCH", "/password-reset", c.Users.PasswordReset, true}, + {"GET", "/password-reset/{token}", c.Users.PasswordResetConfirm, true}, + {"POST", "/reset-token", c.Users.CreateResetToken, true}, + {"POST", "/verification-token", mw.Auth(a, c.Users.CreateEmailVerificationToken, redirectGuest), true}, + {"GET", "/verify-email/{token}", mw.Auth(a, c.Users.VerifyEmail, redirectGuest), true}, + {"PATCH", "/account/profile", mw.Auth(a, c.Users.ProfileUpdate, nil), true}, + {"PATCH", "/account/password", mw.Auth(a, c.Users.PasswordUpdate, nil), true}, + } + + if !a.Config.DisableRegistration { + ret = append(ret, Route{"GET", "/join", c.Users.New, true}) + ret = append(ret, Route{"POST", "/join", c.Users.Create, true}) + } + + return ret +} + +// NewAPIRoutes returns a new api routes +func NewAPIRoutes(a *app.App, c *Controllers) []Route { + + proOnly := mw.AuthParams{ProOnly: true} + + return []Route{ + // internal + {"GET", "/health", c.Health.Index, true}, + + // v3 + {"GET", "/v3/sync/fragment", mw.Cors(mw.Auth(a, c.Sync.GetSyncFragment, &proOnly)), false}, + {"GET", "/v3/sync/state", mw.Cors(mw.Auth(a, c.Sync.GetSyncState, &proOnly)), false}, + {"POST", "/v3/signin", mw.Cors(c.Users.V3Login), true}, + {"POST", "/v3/signout", mw.Cors(c.Users.V3Logout), true}, + {"OPTIONS", "/v3/signout", mw.Cors(c.Users.logoutOptions), true}, + {"GET", "/v3/notes", mw.Cors(mw.Auth(a, c.Notes.V3Index, nil)), true}, + {"GET", "/v3/notes/{noteUUID}", c.Notes.V3Show, true}, + {"POST", "/v3/notes", mw.Cors(mw.Auth(a, c.Notes.V3Create, nil)), true}, + {"DELETE", "/v3/notes/{noteUUID}", mw.Cors(mw.Auth(a, c.Notes.V3Delete, nil)), true}, + {"PATCH", "/v3/notes/{noteUUID}", mw.Cors(mw.Auth(a, c.Notes.V3Update, nil)), true}, + {"OPTIONS", "/v3/notes", mw.Cors(c.Notes.IndexOptions), true}, + {"GET", "/v3/books", mw.Cors(mw.Auth(a, c.Books.V3Index, nil)), true}, + {"GET", "/v3/books/{bookUUID}", mw.Cors(mw.Auth(a, c.Books.V3Show, nil)), true}, + {"POST", "/v3/books", mw.Cors(mw.Auth(a, c.Books.V3Create, nil)), true}, + {"PATCH", "/v3/books/{bookUUID}", mw.Cors(mw.Auth(a, c.Books.V3Update, nil)), true}, + {"DELETE", "/v3/books/{bookUUID}", mw.Cors(mw.Auth(a, c.Books.V3Delete, nil)), true}, + {"OPTIONS", "/v3/books", mw.Cors(c.Books.IndexOptions), true}, + } +} + +func registerRoutes(router *mux.Router, wrapper mw.Middleware, app *app.App, routes []Route) { + for _, route := range routes { + wrappedHandler := wrapper(route.Handler, app, route.RateLimit) + + router. + Handle(route.Pattern, wrappedHandler). + Methods(route.Method) + } +} + +// NewRouter creates and returns a new router +func NewRouter(app *app.App, rc RouteConfig) (http.Handler, error) { + if err := app.Validate(); err != nil { + return nil, errors.Wrap(err, "validating the app parameters") + } + + router := mux.NewRouter().StrictSlash(true) + + webRouter := router.PathPrefix("/").Subrouter() + apiRouter := router.PathPrefix("/api").Subrouter() + registerRoutes(webRouter, mw.WebMw, app, rc.WebRoutes) + registerRoutes(apiRouter, mw.APIMw, app, rc.APIRoutes) + + router.PathPrefix("/api/v1").Handler(mw.ApplyLimit(mw.NotSupported, true)) + router.PathPrefix("/api/v2").Handler(mw.ApplyLimit(mw.NotSupported, true)) + + // static + staticHandler := http.StripPrefix("/static/", http.FileServer(http.Dir(app.Config.StaticDir))) + router.PathPrefix("/static/").Handler(staticHandler) + + router.HandleFunc("/robots.txt", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("User-agent: *\nAllow: /")) + }) + + // catch-all + router.PathPrefix("/").HandlerFunc(rc.Controllers.Static.NotFound) + + return mw.Global(router), nil +} diff --git a/pkg/server/controllers/routes_test.go b/pkg/server/controllers/routes_test.go new file mode 100644 index 00000000..5f09b0d1 --- /dev/null +++ b/pkg/server/controllers/routes_test.go @@ -0,0 +1,77 @@ +/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd + * + * This file is part of Dnote. + * + * Dnote is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Dnote is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Dnote. If not, see . + */ + +package controllers + +import ( + "net/http" + "testing" + + "github.com/dnote/dnote/pkg/assert" + "github.com/dnote/dnote/pkg/clock" + "github.com/dnote/dnote/pkg/server/app" + "github.com/dnote/dnote/pkg/server/config" + "github.com/dnote/dnote/pkg/server/testutils" +) + +func TestNotSupportedVersions(t *testing.T) { + testCases := []struct { + path string + }{ + // v1 + { + path: "/api/v1", + }, + { + path: "/api/v1/foo", + }, + { + path: "/api/v1/bar/baz", + }, + // v2 + { + path: "/api/v2", + }, + { + path: "/api/v2/foo", + }, + { + path: "/api/v2/bar/baz", + }, + } + + // setup + server := MustNewServer(t, &app.App{ + Clock: clock.NewMock(), + Config: config.Config{ + PageTemplateDir: "../views", + }, + }) + defer server.Close() + + for _, tc := range testCases { + t.Run(tc.path, func(t *testing.T) { + // execute + req := testutils.MakeReq(server.URL, "GET", tc.path, "") + res := testutils.HTTPDo(t, req) + + // test + assert.Equal(t, res.StatusCode, http.StatusGone, "status code mismatch") + }) + } +} diff --git a/pkg/server/controllers/static.go b/pkg/server/controllers/static.go new file mode 100644 index 00000000..6641c6fc --- /dev/null +++ b/pkg/server/controllers/static.go @@ -0,0 +1,35 @@ +package controllers + +import ( + "net/http" + "strings" + + "github.com/dnote/dnote/pkg/server/app" + "github.com/dnote/dnote/pkg/server/views" +) + +// NewStatic creates a new Static controller. +func NewStatic(app *app.App, baseDir string) *Static { + return &Static{ + NotFoundView: views.NewView(baseDir, app, views.Config{Title: "Not Found", Layout: "base"}, "static/not_found"), + } +} + +// Static is a static controller +type Static struct { + NotFoundView *views.View +} + +// NotFound is a catch-all handler for requests with no matching handler +func (s *Static) NotFound(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotFound) + + accept := r.Header.Get("Accept") + + if strings.Contains(accept, "text/html") { + s.NotFoundView.Render(w, r, nil, http.StatusOK) + } else { + statusText := http.StatusText(http.StatusNotFound) + w.Write([]byte(statusText)) + } +} diff --git a/pkg/server/api/v3_sync.go b/pkg/server/controllers/sync.go similarity index 83% rename from pkg/server/api/v3_sync.go rename to pkg/server/controllers/sync.go index f30f4273..76e7f742 100644 --- a/pkg/server/api/v3_sync.go +++ b/pkg/server/controllers/sync.go @@ -16,7 +16,7 @@ * along with Dnote. If not, see . */ -package api +package controllers import ( "fmt" @@ -26,13 +26,27 @@ import ( "strconv" "time" + "github.com/dnote/dnote/pkg/server/context" "github.com/dnote/dnote/pkg/server/database" - "github.com/dnote/dnote/pkg/server/handlers" - "github.com/dnote/dnote/pkg/server/helpers" "github.com/dnote/dnote/pkg/server/log" + "github.com/dnote/dnote/pkg/server/middleware" "github.com/pkg/errors" + + "github.com/dnote/dnote/pkg/server/app" ) +// NewSync creates a new Sync controller +func NewSync(app *app.App) *Sync { + return &Sync{ + app: app, + } +} + +// Sync is a sync controller. +type Sync struct { + app *app.App +} + // fullSyncBefore is the system-wide timestamp that represents the point in time // before which clients must perform a full-sync rather than incremental sync. const fullSyncBefore = 0 @@ -121,13 +135,13 @@ func (e *queryParamError) Error() string { return fmt.Sprintf("invalid query param %s=%s. %s", e.key, e.value, e.message) } -func (a *API) newFragment(userID, userMaxUSN, afterUSN, limit int) (SyncFragment, error) { +func (s *Sync) newFragment(userID, userMaxUSN, afterUSN, limit int) (SyncFragment, error) { var notes []database.Note - if err := a.App.DB.Where("user_id = ? AND usn > ? AND usn <= ?", userID, afterUSN, userMaxUSN).Order("usn ASC").Limit(limit).Find(¬es).Error; err != nil { + if err := s.app.DB.Where("user_id = ? AND usn > ? AND usn <= ?", userID, afterUSN, userMaxUSN).Order("usn ASC").Limit(limit).Find(¬es).Error; err != nil { return SyncFragment{}, nil } var books []database.Book - if err := a.App.DB.Where("user_id = ? AND usn > ? AND usn <= ?", userID, afterUSN, userMaxUSN).Order("usn ASC").Limit(limit).Find(&books).Error; err != nil { + if err := s.app.DB.Where("user_id = ? AND usn > ? AND usn <= ?", userID, afterUSN, userMaxUSN).Order("usn ASC").Limit(limit).Find(&books).Error; err != nil { return SyncFragment{}, nil } @@ -192,7 +206,7 @@ func (a *API) newFragment(userID, userMaxUSN, afterUSN, limit int) (SyncFragment ret := SyncFragment{ FragMaxUSN: fragMaxUSN, UserMaxUSN: userMaxUSN, - CurrentTime: a.App.Clock.Now().Unix(), + CurrentTime: s.app.Clock.Now().Unix(), Notes: fragNotes, Books: fragBooks, ExpungedNotes: fragExpungedNotes, @@ -248,29 +262,29 @@ type GetSyncFragmentResp struct { } // GetSyncFragment responds with a sync fragment -func (a *API) GetSyncFragment(w http.ResponseWriter, r *http.Request) { - user, ok := r.Context().Value(helpers.KeyUser).(database.User) - if !ok { - handlers.DoError(w, "No authenticated user found", nil, http.StatusInternalServerError) +func (s *Sync) GetSyncFragment(w http.ResponseWriter, r *http.Request) { + user := context.User(r.Context()) + if user == nil { + middleware.DoError(w, "No authenticated user found", nil, http.StatusInternalServerError) return } afterUSN, limit, err := parseGetSyncFragmentQuery(r.URL.Query()) if err != nil { - handlers.DoError(w, "parsing query params", err, http.StatusInternalServerError) + middleware.DoError(w, "parsing query params", err, http.StatusInternalServerError) return } - fragment, err := a.newFragment(user.ID, user.MaxUSN, afterUSN, limit) + fragment, err := s.newFragment(user.ID, user.MaxUSN, afterUSN, limit) if err != nil { - handlers.DoError(w, "getting fragment", err, http.StatusInternalServerError) + middleware.DoError(w, "getting fragment", err, http.StatusInternalServerError) return } response := GetSyncFragmentResp{ Fragment: fragment, } - handlers.RespondJSON(w, http.StatusOK, response) + respondJSON(w, http.StatusOK, response) } // GetSyncStateResp represents a response from GetSyncFragment handler @@ -281,10 +295,10 @@ type GetSyncStateResp struct { } // GetSyncState responds with a sync fragment -func (a *API) GetSyncState(w http.ResponseWriter, r *http.Request) { - user, ok := r.Context().Value(helpers.KeyUser).(database.User) - if !ok { - handlers.DoError(w, "No authenticated user found", nil, http.StatusInternalServerError) +func (s *Sync) GetSyncState(w http.ResponseWriter, r *http.Request) { + user := context.User(r.Context()) + if user == nil { + middleware.DoError(w, "No authenticated user found", nil, http.StatusInternalServerError) return } @@ -292,7 +306,7 @@ func (a *API) GetSyncState(w http.ResponseWriter, r *http.Request) { FullSyncBefore: fullSyncBefore, MaxUSN: user.MaxUSN, // TODO: exposing server time means we probably shouldn't seed random generator with time? - CurrentTime: a.App.Clock.Now().Unix(), + CurrentTime: s.app.Clock.Now().Unix(), } log.WithFields(log.Fields{ @@ -300,5 +314,5 @@ func (a *API) GetSyncState(w http.ResponseWriter, r *http.Request) { "resp": response, }).Info("getting sync state") - handlers.RespondJSON(w, http.StatusOK, response) + respondJSON(w, http.StatusOK, response) } diff --git a/pkg/server/api/v3_sync_test.go b/pkg/server/controllers/sync_test.go similarity index 99% rename from pkg/server/api/v3_sync_test.go rename to pkg/server/controllers/sync_test.go index 8f2fb6e6..e8e48da1 100644 --- a/pkg/server/api/v3_sync_test.go +++ b/pkg/server/controllers/sync_test.go @@ -16,7 +16,7 @@ * along with Dnote. If not, see . */ -package api +package controllers import ( "fmt" diff --git a/pkg/server/api/testutils.go b/pkg/server/controllers/testutils.go similarity index 67% rename from pkg/server/api/testutils.go rename to pkg/server/controllers/testutils.go index fe17e25c..aaf4f5c7 100644 --- a/pkg/server/api/testutils.go +++ b/pkg/server/controllers/testutils.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd * * This file is part of Dnote. * @@ -16,7 +16,7 @@ * along with Dnote. If not, see . */ -package api +package controllers import ( "net/http/httptest" @@ -29,20 +29,29 @@ import ( // MustNewServer is a test utility function to initialize a new server // with the given app paratmers func MustNewServer(t *testing.T, appParams *app.App) *httptest.Server { - api := NewTestAPI(appParams) - r, err := NewRouter(&api) + server, err := NewServer(appParams) if err != nil { - t.Fatal(errors.Wrap(err, "initializing server")) + t.Fatal(errors.Wrap(err, "initializing router")) } - server := httptest.NewServer(r) - return server } -// NewTestAPI returns a new API for test -func NewTestAPI(appParams *app.App) API { +func NewServer(appParams *app.App) (*httptest.Server, error) { a := app.NewTest(appParams) - return API{App: &a} + ctl := New(&a, a.Config.PageTemplateDir) + rc := RouteConfig{ + WebRoutes: NewWebRoutes(&a, ctl), + APIRoutes: NewAPIRoutes(&a, ctl), + Controllers: ctl, + } + r, err := NewRouter(&a, rc) + if err != nil { + return nil, errors.Wrap(err, "initializing router") + } + + server := httptest.NewServer(r) + + return server, nil } diff --git a/pkg/server/controllers/users.go b/pkg/server/controllers/users.go new file mode 100644 index 00000000..ba8cdca1 --- /dev/null +++ b/pkg/server/controllers/users.go @@ -0,0 +1,718 @@ +package controllers + +import ( + "net/http" + "net/url" + "time" + + "github.com/dnote/dnote/pkg/server/app" + "github.com/dnote/dnote/pkg/server/buildinfo" + "github.com/dnote/dnote/pkg/server/context" + "github.com/dnote/dnote/pkg/server/database" + "github.com/dnote/dnote/pkg/server/helpers" + "github.com/dnote/dnote/pkg/server/log" + "github.com/dnote/dnote/pkg/server/mailer" + "github.com/dnote/dnote/pkg/server/token" + "github.com/dnote/dnote/pkg/server/views" + "github.com/gorilla/mux" + "github.com/pkg/errors" + "golang.org/x/crypto/bcrypt" +) + +var commonHelpers = map[string]interface{}{ + "getPathWithReferrer": func(base string, referrer string) string { + if referrer == "" { + return base + } + + query := url.Values{} + query.Set("referrer", referrer) + + return helpers.GetPath(base, &query) + }, +} + +// NewUsers creates a new Users controller. +// It panics if the necessary templates are not parsed. +func NewUsers(app *app.App, baseDir string) *Users { + return &Users{ + NewView: views.NewView(baseDir, app, + views.Config{Title: "Join", Layout: "base", HelperFuncs: commonHelpers, AlertInBody: true}, + "users/new", + ), + LoginView: views.NewView(baseDir, app, + views.Config{Title: "Sign In", Layout: "base", HelperFuncs: commonHelpers, AlertInBody: true}, + "users/login", + ), + PasswordResetView: views.NewView(baseDir, app, + views.Config{Title: "Reset Password", Layout: "base", HelperFuncs: commonHelpers, AlertInBody: true}, + "users/password_reset", + ), + PasswordResetConfirmView: views.NewView(baseDir, app, + views.Config{Title: "Reset Password", Layout: "base", HelperFuncs: commonHelpers, AlertInBody: true}, + "users/password_reset_confirm", + ), + SettingView: views.NewView(baseDir, app, + views.Config{Layout: "base", HelperFuncs: commonHelpers, HeaderTemplate: "navbar"}, + "users/settings", + ), + AboutView: views.NewView(baseDir, app, + views.Config{Title: "About", Layout: "base", HelperFuncs: commonHelpers, HeaderTemplate: "navbar"}, + "users/settings_about", + ), + EmailVerificationView: views.NewView(baseDir, app, + views.Config{Layout: "base", HelperFuncs: commonHelpers, HeaderTemplate: "navbar"}, + "users/email_verification", + ), + app: app, + } +} + +// Users is a user controller. +type Users struct { + NewView *views.View + LoginView *views.View + SettingView *views.View + AboutView *views.View + PasswordResetView *views.View + PasswordResetConfirmView *views.View + EmailVerificationView *views.View + app *app.App +} + +// New renders user registration page +func (u *Users) New(w http.ResponseWriter, r *http.Request) { + vd := getDataWithReferrer(r) + u.NewView.Render(w, r, &vd, http.StatusOK) +} + +// RegistrationForm is the form data for registering +type RegistrationForm struct { + Email string `schema:"email"` + Password string `schema:"password"` + PasswordConfirmation string `schema:"password_confirmation"` +} + +// Create handles register +func (u *Users) Create(w http.ResponseWriter, r *http.Request) { + vd := getDataWithReferrer(r) + + var form RegistrationForm + if err := parseForm(r, &form); err != nil { + handleHTMLError(w, r, err, "parsing form", u.NewView, vd) + return + } + + vd.Yield["Email"] = form.Email + + user, err := u.app.CreateUser(form.Email, form.Password, form.PasswordConfirmation) + if err != nil { + handleHTMLError(w, r, err, "creating user", u.NewView, vd) + return + } + + session, err := u.app.SignIn(&user) + if err != nil { + handleHTMLError(w, r, err, "signing in a user", u.LoginView, vd) + return + } + + if err := u.app.SendWelcomeEmail(form.Email); err != nil { + log.ErrorWrap(err, "sending welcome email") + } + + setSessionCookie(w, session.Key, session.ExpiresAt) + + dest := getPathOrReferrer("/", r) + http.Redirect(w, r, dest, http.StatusFound) +} + +// LoginForm is the form data for log in +type LoginForm struct { + Email string `schema:"email" json:"email"` + Password string `schema:"password" json:"password"` +} + +func (u *Users) login(form LoginForm) (*database.Session, error) { + if form.Email == "" { + return nil, app.ErrEmailRequired + } + if form.Password == "" { + return nil, app.ErrPasswordRequired + } + + user, err := u.app.Authenticate(form.Email, form.Password) + if err != nil { + // If the user is not found, treat it as invalid login + if err == app.ErrNotFound { + return nil, app.ErrLoginInvalid + } + + return nil, err + } + + s, err := u.app.SignIn(user) + if err != nil { + return nil, err + } + + return s, nil +} + +func getPathOrReferrer(path string, r *http.Request) string { + q := r.URL.Query() + referrer := q.Get("referrer") + + if referrer == "" { + return path + } + + return referrer +} + +func getDataWithReferrer(r *http.Request) views.Data { + vd := views.Data{} + + vd.Yield = map[string]interface{}{ + "Referrer": r.URL.Query().Get("referrer"), + } + + return vd +} + +// NewLogin renders user login page +func (u *Users) NewLogin(w http.ResponseWriter, r *http.Request) { + vd := getDataWithReferrer(r) + u.LoginView.Render(w, r, &vd, http.StatusOK) +} + +// Login handles login +func (u *Users) Login(w http.ResponseWriter, r *http.Request) { + vd := getDataWithReferrer(r) + + var form LoginForm + if err := parseRequestData(r, &form); err != nil { + handleHTMLError(w, r, err, "parsing payload", u.LoginView, vd) + return + } + + session, err := u.login(form) + if err != nil { + vd.Yield["Email"] = form.Email + handleHTMLError(w, r, err, "logging in user", u.LoginView, vd) + return + } + + setSessionCookie(w, session.Key, session.ExpiresAt) + + dest := getPathOrReferrer("/", r) + http.Redirect(w, r, dest, http.StatusFound) +} + +// V3Login handles login +func (u *Users) V3Login(w http.ResponseWriter, r *http.Request) { + var form LoginForm + if err := parseRequestData(r, &form); err != nil { + handleJSONError(w, err, "parsing payload") + return + } + + session, err := u.login(form) + if err != nil { + handleJSONError(w, err, "logging in user") + return + } + + respondWithSession(w, http.StatusOK, session) +} + +func (u *Users) logout(r *http.Request) (bool, error) { + key, err := GetCredential(r) + if err != nil { + return false, errors.Wrap(err, "getting credentials") + } + + if key == "" { + return false, nil + } + + if err = u.app.DeleteSession(key); err != nil { + return false, errors.Wrap(err, "deleting session") + } + + return true, nil +} + +// Logout handles logout +func (u *Users) Logout(w http.ResponseWriter, r *http.Request) { + var vd views.Data + + ok, err := u.logout(r) + if err != nil { + handleHTMLError(w, r, err, "logging out", u.LoginView, vd) + return + } + + if ok { + unsetSessionCookie(w) + } + + http.Redirect(w, r, "/login", http.StatusFound) +} + +// V3Logout handles logout via API +func (u *Users) V3Logout(w http.ResponseWriter, r *http.Request) { + ok, err := u.logout(r) + if err != nil { + handleJSONError(w, err, "logging out") + return + } + + if ok { + unsetSessionCookie(w) + } + + w.WriteHeader(http.StatusNoContent) +} + +type createResetTokenPayload struct { + Email string `schema:"email" json:"email"` +} + +func (u *Users) CreateResetToken(w http.ResponseWriter, r *http.Request) { + vd := views.Data{} + + var form createResetTokenPayload + if err := parseForm(r, &form); err != nil { + handleHTMLError(w, r, err, "parsing form", u.PasswordResetView, vd) + return + } + + if form.Email == "" { + handleHTMLError(w, r, app.ErrEmailRequired, "email is not provided", u.PasswordResetView, vd) + return + } + + var account database.Account + conn := u.app.DB.Where("email = ?", form.Email).First(&account) + if conn.RecordNotFound() { + return + } + if err := conn.Error; err != nil { + handleHTMLError(w, r, err, "finding account", u.PasswordResetView, vd) + return + } + + resetToken, err := token.Create(u.app.DB, account.UserID, database.TokenTypeResetPassword) + if err != nil { + handleHTMLError(w, r, err, "generating token", u.PasswordResetView, vd) + return + } + + if err := u.app.SendPasswordResetEmail(account.Email.String, resetToken.Value); err != nil { + handleHTMLError(w, r, err, "sending password reset email", u.PasswordResetView, vd) + return + } + + alert := views.Alert{ + Level: views.AlertLvlSuccess, + Message: "Check your email for a link to reset your password.", + } + views.RedirectAlert(w, r, "/password-reset", http.StatusFound, alert) +} + +// PasswordResetConfirm renders password reset view +func (u *Users) PasswordResetConfirm(w http.ResponseWriter, r *http.Request) { + vd := views.Data{} + + vars := mux.Vars(r) + token := vars["token"] + + vd.Yield = map[string]interface{}{ + "Token": token, + } + + u.PasswordResetConfirmView.Render(w, r, &vd, http.StatusOK) +} + +type resetPasswordPayload struct { + Password string `schema:"password" json:"password"` + PasswordConfirmation string `schema:"password_confirmation" json:"password_confirmation"` + Token string `schema:"token" json:"token"` +} + +// PasswordReset renders password reset view +func (u *Users) PasswordReset(w http.ResponseWriter, r *http.Request) { + vd := views.Data{} + + var params resetPasswordPayload + if err := parseForm(r, ¶ms); err != nil { + handleHTMLError(w, r, err, "parsing params", u.NewView, vd) + return + } + + vd.Yield = map[string]interface{}{ + "Token": params.Token, + } + + if params.Password != params.PasswordConfirmation { + handleHTMLError(w, r, app.ErrPasswordConfirmationMismatch, "password mismatch", u.PasswordResetConfirmView, vd) + return + } + + var token database.Token + conn := u.app.DB.Where("value = ? AND type =? AND used_at IS NULL", params.Token, database.TokenTypeResetPassword).First(&token) + if conn.RecordNotFound() { + handleHTMLError(w, r, app.ErrInvalidToken, "invalid token", u.PasswordResetConfirmView, vd) + return + } + if err := conn.Error; err != nil { + handleHTMLError(w, r, err, "finding token", u.PasswordResetConfirmView, vd) + return + } + + if token.UsedAt != nil { + handleHTMLError(w, r, app.ErrInvalidToken, "invalid token", u.PasswordResetConfirmView, vd) + return + } + + // Expire after 10 minutes + if time.Since(token.CreatedAt).Minutes() > 10 { + handleHTMLError(w, r, app.ErrPasswordResetTokenExpired, "expired token", u.PasswordResetConfirmView, vd) + return + } + + tx := u.app.DB.Begin() + + hashedPassword, err := bcrypt.GenerateFromPassword([]byte(params.Password), bcrypt.DefaultCost) + if err != nil { + tx.Rollback() + handleHTMLError(w, r, err, "hashing password", u.PasswordResetConfirmView, vd) + return + } + + var account database.Account + if err := u.app.DB.Where("user_id = ?", token.UserID).First(&account).Error; err != nil { + tx.Rollback() + handleHTMLError(w, r, err, "finding user", u.PasswordResetConfirmView, vd) + return + } + + if err := tx.Model(&account).Update("password", string(hashedPassword)).Error; err != nil { + tx.Rollback() + handleHTMLError(w, r, err, "updating password", u.PasswordResetConfirmView, vd) + return + } + if err := tx.Model(&token).Update("used_at", time.Now()).Error; err != nil { + tx.Rollback() + handleHTMLError(w, r, err, "updating password reset token", u.PasswordResetConfirmView, vd) + return + } + + if err := u.app.DeleteUserSessions(tx, account.UserID); err != nil { + tx.Rollback() + handleHTMLError(w, r, err, "deleting user sessions", u.PasswordResetConfirmView, vd) + return + } + + tx.Commit() + + var user database.User + if err := u.app.DB.Where("id = ?", account.UserID).First(&user).Error; err != nil { + handleHTMLError(w, r, err, "finding user", u.PasswordResetConfirmView, vd) + return + } + + alert := views.Alert{ + Level: views.AlertLvlSuccess, + Message: "Password reset successful", + } + views.RedirectAlert(w, r, "/login", http.StatusFound, alert) + + if err := u.app.SendPasswordResetAlertEmail(account.Email.String); err != nil { + log.ErrorWrap(err, "sending password reset email") + } +} + +func (u *Users) logoutOptions(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Access-Control-Allow-Methods", "POST") + w.Header().Set("Access-Control-Allow-Headers", "Authorization, Version") +} + +func (u *Users) Settings(w http.ResponseWriter, r *http.Request) { + vd := views.Data{} + + u.SettingView.Render(w, r, &vd, http.StatusOK) +} + +func (u *Users) About(w http.ResponseWriter, r *http.Request) { + vd := views.Data{} + + vd.Yield = map[string]interface{}{ + "Version": buildinfo.Version, + } + + u.AboutView.Render(w, r, &vd, http.StatusOK) +} + +type updatePasswordForm struct { + OldPassword string `schema:"old_password"` + NewPassword string `schema:"new_password"` + NewPasswordConfirmation string `schema:"new_password_confirmation"` +} + +func (u *Users) PasswordUpdate(w http.ResponseWriter, r *http.Request) { + vd := views.Data{} + + user := context.User(r.Context()) + if user == nil { + handleHTMLError(w, r, app.ErrLoginRequired, "No authenticated user found", u.SettingView, vd) + return + } + + var form updatePasswordForm + if err := parseRequestData(r, &form); err != nil { + handleHTMLError(w, r, err, "parsing payload", u.LoginView, vd) + return + } + + if form.OldPassword == "" || form.NewPassword == "" { + handleHTMLError(w, r, app.ErrInvalidPasswordChangeInput, "invalid params", u.SettingView, vd) + return + } + if form.NewPassword != form.NewPasswordConfirmation { + handleHTMLError(w, r, app.ErrPasswordConfirmationMismatch, "passwords do not match", u.SettingView, vd) + return + } + + var account database.Account + if err := u.app.DB.Where("user_id = ?", user.ID).First(&account).Error; err != nil { + handleHTMLError(w, r, err, "getting account", u.SettingView, vd) + return + } + + password := []byte(form.OldPassword) + if err := bcrypt.CompareHashAndPassword([]byte(account.Password.String), password); err != nil { + log.WithFields(log.Fields{ + "user_id": user.ID, + }).Warn("invalid password update attempt") + handleHTMLError(w, r, app.ErrInvalidPassword, "invalid password", u.SettingView, vd) + return + } + + if err := validatePassword(form.NewPassword); err != nil { + handleHTMLError(w, r, err, "invalid password", u.SettingView, vd) + return + } + + hashedNewPassword, err := bcrypt.GenerateFromPassword([]byte(form.NewPassword), bcrypt.DefaultCost) + if err != nil { + handleHTMLError(w, r, err, "hashing password", u.SettingView, vd) + return + } + + if err := u.app.DB.Model(&account).Update("password", string(hashedNewPassword)).Error; err != nil { + handleHTMLError(w, r, err, "updating password", u.SettingView, vd) + return + } + + alert := views.Alert{ + Level: views.AlertLvlSuccess, + Message: "Password change successful", + } + views.RedirectAlert(w, r, "/", http.StatusFound, alert) +} + +func validatePassword(password string) error { + if len(password) < 8 { + return app.ErrPasswordTooShort + } + + return nil +} + +type updateProfileForm struct { + Email string `schema:"email"` + Password string `schema:"password"` +} + +func (u *Users) ProfileUpdate(w http.ResponseWriter, r *http.Request) { + vd := views.Data{} + + user := context.User(r.Context()) + if user == nil { + handleHTMLError(w, r, app.ErrLoginRequired, "No authenticated user found", u.SettingView, vd) + return + } + + var account database.Account + if err := u.app.DB.Where("user_id = ?", user.ID).First(&account).Error; err != nil { + handleHTMLError(w, r, err, "getting account", u.SettingView, vd) + return + } + + var form updateProfileForm + if err := parseRequestData(r, &form); err != nil { + handleHTMLError(w, r, err, "parsing payload", u.SettingView, vd) + return + } + + password := []byte(form.Password) + if err := bcrypt.CompareHashAndPassword([]byte(account.Password.String), password); err != nil { + log.WithFields(log.Fields{ + "user_id": user.ID, + }).Warn("invalid email update attempt") + handleHTMLError(w, r, app.ErrInvalidPassword, "Wrong password", u.SettingView, vd) + return + } + + // Validate + if len(form.Email) > 60 { + handleHTMLError(w, r, app.ErrEmailTooLong, "Email is too long", u.SettingView, vd) + return + } + + tx := u.app.DB.Begin() + if err := tx.Save(&user).Error; err != nil { + tx.Rollback() + handleHTMLError(w, r, err, "saving user", u.SettingView, vd) + return + } + + // check if email was changed + if form.Email != account.Email.String { + account.EmailVerified = false + } + account.Email.String = form.Email + + if err := tx.Save(&account).Error; err != nil { + tx.Rollback() + handleHTMLError(w, r, err, "saving account", u.SettingView, vd) + return + } + + tx.Commit() + + alert := views.Alert{ + Level: views.AlertLvlSuccess, + Message: "Email change successful", + } + views.RedirectAlert(w, r, "/", http.StatusFound, alert) +} + +func (u *Users) VerifyEmail(w http.ResponseWriter, r *http.Request) { + vd := views.Data{} + + vars := mux.Vars(r) + tokenValue := vars["token"] + + if tokenValue == "" { + handleHTMLError(w, r, app.ErrMissingToken, "Missing email verification token", u.EmailVerificationView, vd) + return + } + + var token database.Token + if err := u.app.DB. + Where("value = ? AND type = ?", tokenValue, database.TokenTypeEmailVerification). + First(&token).Error; err != nil { + handleHTMLError(w, r, app.ErrInvalidToken, "Finding token", u.EmailVerificationView, vd) + return + } + + if token.UsedAt != nil { + handleHTMLError(w, r, app.ErrInvalidToken, "Token has already been used.", u.EmailVerificationView, vd) + return + } + + // Expire after ttl + if time.Since(token.CreatedAt).Minutes() > 30 { + handleHTMLError(w, r, app.ErrExpiredToken, "Token has expired.", u.EmailVerificationView, vd) + return + } + + var account database.Account + if err := u.app.DB.Where("user_id = ?", token.UserID).First(&account).Error; err != nil { + handleHTMLError(w, r, err, "finding account", u.EmailVerificationView, vd) + return + } + if account.EmailVerified { + handleHTMLError(w, r, app.ErrEmailAlreadyVerified, "Already verified", u.EmailVerificationView, vd) + return + } + + tx := u.app.DB.Begin() + account.EmailVerified = true + if err := tx.Save(&account).Error; err != nil { + tx.Rollback() + handleHTMLError(w, r, err, "updating email_verified", u.EmailVerificationView, vd) + return + } + if err := tx.Model(&token).Update("used_at", time.Now()).Error; err != nil { + tx.Rollback() + handleHTMLError(w, r, err, "updating reset token", u.EmailVerificationView, vd) + return + } + tx.Commit() + + var user database.User + if err := u.app.DB.Where("id = ?", token.UserID).First(&user).Error; err != nil { + handleHTMLError(w, r, err, "finding user", u.EmailVerificationView, vd) + return + } + + session, err := u.app.SignIn(&user) + if err != nil { + handleHTMLError(w, r, err, "Creating session", u.EmailVerificationView, vd) + } + + setSessionCookie(w, session.Key, session.ExpiresAt) + http.Redirect(w, r, "/", http.StatusFound) +} + +func (u *Users) CreateEmailVerificationToken(w http.ResponseWriter, r *http.Request) { + vd := views.Data{} + + user := context.User(r.Context()) + if user == nil { + handleHTMLError(w, r, app.ErrLoginRequired, "No authenticated user found", u.SettingView, vd) + return + } + + var account database.Account + err := u.app.DB.Where("user_id = ?", user.ID).First(&account).Error + if err != nil { + handleHTMLError(w, r, err, "finding account", u.SettingView, vd) + return + } + + if account.EmailVerified { + handleHTMLError(w, r, app.ErrEmailAlreadyVerified, "email is already verified.", u.SettingView, vd) + return + } + if account.Email.String == "" { + handleHTMLError(w, r, app.ErrEmailRequired, "email is empty.", u.SettingView, vd) + return + } + + tok, err := token.Create(u.app.DB, account.UserID, database.TokenTypeEmailVerification) + if err != nil { + handleHTMLError(w, r, err, "saving token", u.SettingView, vd) + return + } + + if err := u.app.SendVerificationEmail(account.Email.String, tok.Value); err != nil { + if errors.Cause(err) == mailer.ErrSMTPNotConfigured { + handleHTMLError(w, r, app.ErrInvalidSMTPConfig, "SMTP config is not configured correctly.", u.SettingView, vd) + } else { + handleHTMLError(w, r, err, "sending verification email", u.SettingView, vd) + } + + return + } + + alert := views.Alert{ + Level: views.AlertLvlSuccess, + Message: "Please check your email for the verification", + } + views.RedirectAlert(w, r, "/", http.StatusFound, alert) +} diff --git a/pkg/server/controllers/users_test.go b/pkg/server/controllers/users_test.go new file mode 100644 index 00000000..110e61b1 --- /dev/null +++ b/pkg/server/controllers/users_test.go @@ -0,0 +1,1388 @@ +/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd + * + * This file is part of Dnote. + * + * Dnote is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Dnote is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Dnote. If not, see . + */ + +package controllers + +import ( + "encoding/json" + "fmt" + "net/http" + "net/http/httptest" + "net/url" + "testing" + "time" + + "github.com/dnote/dnote/pkg/assert" + "github.com/dnote/dnote/pkg/clock" + "github.com/dnote/dnote/pkg/server/app" + "github.com/dnote/dnote/pkg/server/config" + "github.com/dnote/dnote/pkg/server/database" + "github.com/dnote/dnote/pkg/server/testutils" + "github.com/pkg/errors" + "golang.org/x/crypto/bcrypt" +) + +func assertResponseSessionCookie(t *testing.T, res *http.Response) { + var sessionCount int + var session database.Session + testutils.MustExec(t, testutils.DB.Model(&database.Session{}).Count(&sessionCount), "counting session") + testutils.MustExec(t, testutils.DB.First(&session), "getting session") + + c := testutils.GetCookieByName(res.Cookies(), "id") + assert.Equal(t, c.Value, session.Key, "session key mismatch") + assert.Equal(t, c.Path, "/", "session path mismatch") + assert.Equal(t, c.HttpOnly, true, "session HTTPOnly mismatch") + assert.Equal(t, c.Expires.Unix(), session.ExpiresAt.Unix(), "session Expires mismatch") +} + +func TestJoin(t *testing.T) { + testCases := []struct { + email string + password string + passwordConfirmation string + onPremise bool + expectedPro bool + }{ + { + email: "alice@example.com", + password: "pass1234", + passwordConfirmation: "pass1234", + onPremise: false, + expectedPro: false, + }, + { + email: "bob@example.com", + password: "Y9EwmjH@Jq6y5a64MSACUoM4w7SAhzvY", + passwordConfirmation: "Y9EwmjH@Jq6y5a64MSACUoM4w7SAhzvY", + onPremise: false, + expectedPro: false, + }, + { + email: "chuck@example.com", + password: "e*H@kJi^vXbWEcD9T5^Am!Y@7#Po2@PC", + passwordConfirmation: "e*H@kJi^vXbWEcD9T5^Am!Y@7#Po2@PC", + onPremise: false, + expectedPro: false, + }, + // on premise + { + email: "dan@example.com", + password: "e*H@kJi^vXbWEcD9T5^Am!Y@7#Po2@PC", + passwordConfirmation: "e*H@kJi^vXbWEcD9T5^Am!Y@7#Po2@PC", + onPremise: true, + expectedPro: true, + }, + } + + for _, tc := range testCases { + t.Run(fmt.Sprintf("register %s %s", tc.email, tc.password), func(t *testing.T) { + defer testutils.ClearData(testutils.DB) + + // Setup + emailBackend := testutils.MockEmailbackendImplementation{} + server := MustNewServer(t, &app.App{ + Clock: clock.NewMock(), + EmailBackend: &emailBackend, + Config: config.Config{ + OnPremise: tc.onPremise, + PageTemplateDir: "../views", + }, + }) + defer server.Close() + + dat := url.Values{} + dat.Set("email", tc.email) + dat.Set("password", tc.password) + dat.Set("password_confirmation", tc.passwordConfirmation) + req := testutils.MakeFormReq(server.URL, "POST", "/join", dat) + + // Execute + res := testutils.HTTPDo(t, req) + + // Test + assert.StatusCodeEquals(t, res, http.StatusFound, "") + + var account database.Account + testutils.MustExec(t, testutils.DB.Where("email = ?", tc.email).First(&account), "finding account") + assert.Equal(t, account.Email.String, tc.email, "Email mismatch") + assert.NotEqual(t, account.UserID, 0, "UserID mismatch") + passwordErr := bcrypt.CompareHashAndPassword([]byte(account.Password.String), []byte(tc.password)) + assert.Equal(t, passwordErr, nil, "Password mismatch") + + var user database.User + testutils.MustExec(t, testutils.DB.Where("id = ?", account.UserID).First(&user), "finding user") + assert.Equal(t, user.Cloud, tc.expectedPro, "Cloud mismatch") + assert.Equal(t, user.MaxUSN, 0, "MaxUSN mismatch") + + // welcome email + assert.Equalf(t, len(emailBackend.Emails), 1, "email queue count mismatch") + assert.DeepEqual(t, emailBackend.Emails[0].To, []string{tc.email}, "email to mismatch") + + // after register, should sign in user + assertResponseSessionCookie(t, res) + }) + } +} + +func TestJoinError(t *testing.T) { + t.Run("missing email", func(t *testing.T) { + defer testutils.ClearData(testutils.DB) + + // Setup + server := MustNewServer(t, &app.App{ + Clock: clock.NewMock(), + Config: config.Config{ + PageTemplateDir: "../views", + }, + }) + defer server.Close() + + dat := url.Values{} + dat.Set("password", "SLMZFM5RmSjA5vfXnG5lPOnrpZSbtmV76cnAcrlr2yU") + req := testutils.MakeFormReq(server.URL, "POST", "/join", dat) + + // Execute + res := testutils.HTTPDo(t, req) + + // Test + assert.StatusCodeEquals(t, res, http.StatusBadRequest, "Status mismatch") + + var accountCount, userCount int + testutils.MustExec(t, testutils.DB.Model(&database.Account{}).Count(&accountCount), "counting account") + testutils.MustExec(t, testutils.DB.Model(&database.User{}).Count(&userCount), "counting user") + + assert.Equal(t, accountCount, 0, "accountCount mismatch") + assert.Equal(t, userCount, 0, "userCount mismatch") + }) + + t.Run("missing password", func(t *testing.T) { + defer testutils.ClearData(testutils.DB) + + // Setup + server := MustNewServer(t, &app.App{ + Clock: clock.NewMock(), + Config: config.Config{ + PageTemplateDir: "../views", + }, + }) + defer server.Close() + + dat := url.Values{} + dat.Set("email", "alice@example.com") + req := testutils.MakeFormReq(server.URL, "POST", "/join", dat) + + // Execute + res := testutils.HTTPDo(t, req) + + // Test + assert.StatusCodeEquals(t, res, http.StatusBadRequest, "Status mismatch") + + var accountCount, userCount int + testutils.MustExec(t, testutils.DB.Model(&database.Account{}).Count(&accountCount), "counting account") + testutils.MustExec(t, testutils.DB.Model(&database.User{}).Count(&userCount), "counting user") + + assert.Equal(t, accountCount, 0, "accountCount mismatch") + assert.Equal(t, userCount, 0, "userCount mismatch") + }) + + t.Run("password confirmation mismatch", func(t *testing.T) { + defer testutils.ClearData(testutils.DB) + + // Setup + server := MustNewServer(t, &app.App{ + Clock: clock.NewMock(), + Config: config.Config{ + PageTemplateDir: "../views", + }, + }) + defer server.Close() + + dat := url.Values{} + dat.Set("email", "alice@example.com") + dat.Set("password", "pass1234") + dat.Set("password_confirmation", "1234pass") + req := testutils.MakeFormReq(server.URL, "POST", "/join", dat) + + // Execute + res := testutils.HTTPDo(t, req) + + // Test + assert.StatusCodeEquals(t, res, http.StatusBadRequest, "Status mismatch") + + var accountCount, userCount int + testutils.MustExec(t, testutils.DB.Model(&database.Account{}).Count(&accountCount), "counting account") + testutils.MustExec(t, testutils.DB.Model(&database.User{}).Count(&userCount), "counting user") + + assert.Equal(t, accountCount, 0, "accountCount mismatch") + assert.Equal(t, userCount, 0, "userCount mismatch") + }) +} + +func TestJoinDuplicateEmail(t *testing.T) { + defer testutils.ClearData(testutils.DB) + + // Setup + server := MustNewServer(t, &app.App{ + Clock: clock.NewMock(), + Config: config.Config{ + PageTemplateDir: "../views", + }, + }) + defer server.Close() + + u := testutils.SetupUserData() + testutils.SetupAccountData(u, "alice@example.com", "somepassword") + + dat := url.Values{} + dat.Set("email", "alice@example.com") + dat.Set("password", "foobarbaz") + dat.Set("password_confirmation", "foobarbaz") + req := testutils.MakeFormReq(server.URL, "POST", "/join", dat) + + // Execute + res := testutils.HTTPDo(t, req) + + // Test + assert.StatusCodeEquals(t, res, http.StatusBadRequest, "status code mismatch") + + var accountCount, userCount, verificationTokenCount int + testutils.MustExec(t, testutils.DB.Model(&database.Account{}).Count(&accountCount), "counting account") + testutils.MustExec(t, testutils.DB.Model(&database.User{}).Count(&userCount), "counting user") + testutils.MustExec(t, testutils.DB.Model(&database.Token{}).Count(&verificationTokenCount), "counting verification token") + + var user database.User + testutils.MustExec(t, testutils.DB.Where("id = ?", u.ID).First(&user), "finding user") + + assert.Equal(t, accountCount, 1, "account count mismatch") + assert.Equal(t, userCount, 1, "user count mismatch") + assert.Equal(t, verificationTokenCount, 0, "verification_token should not have been created") + assert.Equal(t, user.LastLoginAt, (*time.Time)(nil), "LastLoginAt mismatch") +} + +func TestJoinDisabled(t *testing.T) { + defer testutils.ClearData(testutils.DB) + + // Setup + server := MustNewServer(t, &app.App{ + Clock: clock.NewMock(), + Config: config.Config{ + PageTemplateDir: "../views", + DisableRegistration: true, + }, + }) + defer server.Close() + + dat := url.Values{} + dat.Set("email", "alice@example.com") + dat.Set("password", "foobarbaz") + req := testutils.MakeFormReq(server.URL, "POST", "/join", dat) + + // Execute + res := testutils.HTTPDo(t, req) + + // Test + assert.StatusCodeEquals(t, res, http.StatusNotFound, "status code mismatch") + + var accountCount, userCount int + testutils.MustExec(t, testutils.DB.Model(&database.Account{}).Count(&accountCount), "counting account") + testutils.MustExec(t, testutils.DB.Model(&database.User{}).Count(&userCount), "counting user") + + assert.Equal(t, accountCount, 0, "account count mismatch") + assert.Equal(t, userCount, 0, "user count mismatch") +} + +func TestLogin(t *testing.T) { + testutils.RunForWebAndAPI(t, "success", func(t *testing.T, target testutils.EndpointType) { + defer testutils.ClearData(testutils.DB) + + // Setup + server := MustNewServer(t, &app.App{ + Clock: clock.NewMock(), + Config: config.Config{ + PageTemplateDir: "../views", + }, + }) + + u := testutils.SetupUserData() + testutils.SetupAccountData(u, "alice@example.com", "pass1234") + defer server.Close() + + // Execute + var req *http.Request + if target == testutils.EndpointWeb { + dat := url.Values{} + dat.Set("email", "alice@example.com") + dat.Set("password", "pass1234") + req = testutils.MakeFormReq(server.URL, "POST", "/login", dat) + } else { + dat := `{"email": "alice@example.com", "password": "pass1234"}` + req = testutils.MakeReq(server.URL, "POST", "/api/v3/signin", dat) + } + + res := testutils.HTTPDo(t, req) + + // Test + if target == testutils.EndpointWeb { + assert.StatusCodeEquals(t, res, http.StatusFound, "") + } else { + assert.StatusCodeEquals(t, res, http.StatusOK, "") + } + + var user database.User + testutils.MustExec(t, testutils.DB.Model(&database.User{}).First(&user), "finding user") + assert.NotEqual(t, user.LastLoginAt, nil, "LastLoginAt mismatch") + + if target == testutils.EndpointWeb { + assertResponseSessionCookie(t, res) + } else { + // after register, should sign in user + var got SessionResponse + if err := json.NewDecoder(res.Body).Decode(&got); err != nil { + t.Fatal(errors.Wrap(err, "decoding payload")) + } + + var sessionCount int + var session database.Session + testutils.MustExec(t, testutils.DB.Model(&database.Session{}).Count(&sessionCount), "counting session") + testutils.MustExec(t, testutils.DB.First(&session), "getting session") + + assert.Equal(t, sessionCount, 1, "sessionCount mismatch") + assert.Equal(t, got.Key, session.Key, "session Key mismatch") + assert.Equal(t, got.ExpiresAt, session.ExpiresAt.Unix(), "session ExpiresAt mismatch") + + assertResponseSessionCookie(t, res) + } + }) + + testutils.RunForWebAndAPI(t, "wrong password", func(t *testing.T, target testutils.EndpointType) { + defer testutils.ClearData(testutils.DB) + + // Setup + server := MustNewServer(t, &app.App{ + Clock: clock.NewMock(), + Config: config.Config{ + PageTemplateDir: "../views", + }, + }) + + u := testutils.SetupUserData() + testutils.SetupAccountData(u, "alice@example.com", "pass1234") + defer server.Close() + + var req *http.Request + if target == testutils.EndpointWeb { + dat := url.Values{} + dat.Set("email", "alice@example.com") + dat.Set("password", "wrongpassword1234") + req = testutils.MakeFormReq(server.URL, "POST", "/login", dat) + } else { + dat := `{"email": "alice@example.com", "password": "wrongpassword1234"}` + req = testutils.MakeReq(server.URL, "POST", "/api/v3/signin", dat) + } + + // Execute + res := testutils.HTTPDo(t, req) + + // Test + assert.StatusCodeEquals(t, res, http.StatusUnauthorized, "") + + var user database.User + testutils.MustExec(t, testutils.DB.Model(&database.User{}).First(&user), "finding user") + assert.Equal(t, user.LastLoginAt, (*time.Time)(nil), "LastLoginAt mismatch") + + var sessionCount int + testutils.MustExec(t, testutils.DB.Model(&database.Session{}).Count(&sessionCount), "counting session") + assert.Equal(t, sessionCount, 0, "sessionCount mismatch") + }) + + testutils.RunForWebAndAPI(t, "wrong email", func(t *testing.T, target testutils.EndpointType) { + defer testutils.ClearData(testutils.DB) + + // Setup + server := MustNewServer(t, &app.App{ + Clock: clock.NewMock(), + Config: config.Config{ + PageTemplateDir: "../views", + }, + }) + defer server.Close() + + u := testutils.SetupUserData() + testutils.SetupAccountData(u, "alice@example.com", "pass1234") + + var req *http.Request + if target == testutils.EndpointWeb { + dat := url.Values{} + dat.Set("email", "bob@example.com") + dat.Set("password", "foobarbaz") + req = testutils.MakeFormReq(server.URL, "POST", "/login", dat) + } else { + dat := `{"email": "bob@example.com", "password": "foobarbaz"}` + req = testutils.MakeReq(server.URL, "POST", "/api/v3/signin", dat) + } + + // Execute + res := testutils.HTTPDo(t, req) + + // Test + assert.StatusCodeEquals(t, res, http.StatusUnauthorized, "") + + var user database.User + testutils.MustExec(t, testutils.DB.Model(&database.User{}).First(&user), "finding user") + assert.DeepEqual(t, user.LastLoginAt, (*time.Time)(nil), "LastLoginAt mismatch") + + var sessionCount int + testutils.MustExec(t, testutils.DB.Model(&database.Session{}).Count(&sessionCount), "counting session") + assert.Equal(t, sessionCount, 0, "sessionCount mismatch") + }) + + testutils.RunForWebAndAPI(t, "nonexistent email", func(t *testing.T, target testutils.EndpointType) { + defer testutils.ClearData(testutils.DB) + + // Setup + server := MustNewServer(t, &app.App{ + Clock: clock.NewMock(), + Config: config.Config{ + PageTemplateDir: "../views", + }, + }) + defer server.Close() + + var req *http.Request + if target == testutils.EndpointWeb { + dat := url.Values{} + dat.Set("email", "nonexistent@example.com") + dat.Set("password", "pass1234") + req = testutils.MakeFormReq(server.URL, "POST", "/login", dat) + } else { + dat := `{"email": "nonexistent@example.com", "password": "pass1234"}` + req = testutils.MakeReq(server.URL, "POST", "/api/v3/signin", dat) + } + + // Execute + res := testutils.HTTPDo(t, req) + + // Test + assert.StatusCodeEquals(t, res, http.StatusUnauthorized, "") + + var sessionCount int + testutils.MustExec(t, testutils.DB.Model(&database.Session{}).Count(&sessionCount), "counting session") + assert.Equal(t, sessionCount, 0, "sessionCount mismatch") + }) +} + +func TestLogout(t *testing.T) { + setupLogoutTest := func(t *testing.T) (*httptest.Server, *database.Session, *database.Session) { + // Setup + server := MustNewServer(t, &app.App{ + Clock: clock.NewMock(), + Config: config.Config{ + PageTemplateDir: "../views", + }, + }) + + aliceUser := testutils.SetupUserData() + testutils.SetupAccountData(aliceUser, "alice@example.com", "pass1234") + anotherUser := testutils.SetupUserData() + + session1ExpiresAt := time.Now().Add(time.Hour * 24) + session1 := database.Session{ + Key: "A9xgggqzTHETy++GDi1NpDNe0iyqosPm9bitdeNGkJU=", + UserID: aliceUser.ID, + ExpiresAt: session1ExpiresAt, + } + testutils.MustExec(t, testutils.DB.Save(&session1), "preparing session1") + session2 := database.Session{ + Key: "MDCpbvCRg7W2sH6S870wqLqZDZTObYeVd0PzOekfo/A=", + UserID: anotherUser.ID, + ExpiresAt: time.Now().Add(time.Hour * 24), + } + testutils.MustExec(t, testutils.DB.Save(&session2), "preparing session2") + + return server, &session1, &session2 + } + + testutils.RunForWebAndAPI(t, "authenticated", func(t *testing.T, target testutils.EndpointType) { + defer testutils.ClearData(testutils.DB) + + server, session1, _ := setupLogoutTest(t) + defer server.Close() + + // Execute + var req *http.Request + if target == testutils.EndpointWeb { + dat := url.Values{} + req = testutils.MakeFormReq(server.URL, "POST", "/logout", dat) + req.AddCookie(&http.Cookie{Name: "id", Value: "A9xgggqzTHETy++GDi1NpDNe0iyqosPm9bitdeNGkJU=", Expires: session1.ExpiresAt, Path: "/", HttpOnly: true}) + } else { + req = testutils.MakeReq(server.URL, "POST", "/api/v3/signout", "") + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", session1.Key)) + } + + res := testutils.HTTPDo(t, req) + + // Test + if target == testutils.EndpointWeb { + assert.StatusCodeEquals(t, res, http.StatusFound, "Status mismatch") + } else { + assert.StatusCodeEquals(t, res, http.StatusNoContent, "Status mismatch") + } + + var sessionCount int + var s2 database.Session + testutils.MustExec(t, testutils.DB.Model(&database.Session{}).Count(&sessionCount), "counting session") + testutils.MustExec(t, testutils.DB.Where("key = ?", "MDCpbvCRg7W2sH6S870wqLqZDZTObYeVd0PzOekfo/A=").First(&s2), "getting s2") + + assert.Equal(t, sessionCount, 1, "sessionCount mismatch") + + if target == testutils.EndpointWeb { + c := testutils.GetCookieByName(res.Cookies(), "id") + assert.Equal(t, c.Value, "", "session key mismatch") + assert.Equal(t, c.Path, "/", "session path mismatch") + assert.Equal(t, c.HttpOnly, true, "session HTTPOnly mismatch") + if c.Expires.After(time.Now()) { + t.Error("session cookie is not expired") + } + } + }) + + testutils.RunForWebAndAPI(t, "unauthenticated", func(t *testing.T, target testutils.EndpointType) { + defer testutils.ClearData(testutils.DB) + + server, _, _ := setupLogoutTest(t) + defer server.Close() + + // Execute + var req *http.Request + if target == testutils.EndpointWeb { + dat := url.Values{} + req = testutils.MakeFormReq(server.URL, "POST", "/logout", dat) + } else { + req = testutils.MakeReq(server.URL, "POST", "/api/v3/signout", "") + } + + res := testutils.HTTPDo(t, req) + + // Test + if target == testutils.EndpointWeb { + assert.StatusCodeEquals(t, res, http.StatusFound, "Status mismatch") + } else { + assert.StatusCodeEquals(t, res, http.StatusNoContent, "Status mismatch") + } + + var sessionCount int + var postSession1, postSession2 database.Session + testutils.MustExec(t, testutils.DB.Model(&database.Session{}).Count(&sessionCount), "counting session") + testutils.MustExec(t, testutils.DB.Where("key = ?", "A9xgggqzTHETy++GDi1NpDNe0iyqosPm9bitdeNGkJU=").First(&postSession1), "getting postSession1") + testutils.MustExec(t, testutils.DB.Where("key = ?", "MDCpbvCRg7W2sH6S870wqLqZDZTObYeVd0PzOekfo/A=").First(&postSession2), "getting postSession2") + + // two existing sessions should remain + assert.Equal(t, sessionCount, 2, "sessionCount mismatch") + + c := testutils.GetCookieByName(res.Cookies(), "id") + assert.Equal(t, c, (*http.Cookie)(nil), "id cookie should have not been set") + }) +} + +func TestResetPassword(t *testing.T) { + t.Run("success", func(t *testing.T) { + defer testutils.ClearData(testutils.DB) + + // Setup + server := MustNewServer(t, &app.App{ + Clock: clock.NewMock(), + Config: config.Config{ + PageTemplateDir: "../views", + }, + }) + defer server.Close() + + u := testutils.SetupUserData() + a := testutils.SetupAccountData(u, "alice@example.com", "oldpassword") + tok := database.Token{ + UserID: u.ID, + Value: "MivFxYiSMMA4An9dP24DNQ==", + Type: database.TokenTypeResetPassword, + } + testutils.MustExec(t, testutils.DB.Save(&tok), "preparing token") + otherTok := database.Token{ + UserID: u.ID, + Value: "somerandomvalue", + Type: database.TokenTypeEmailVerification, + } + testutils.MustExec(t, testutils.DB.Save(&otherTok), "preparing another token") + + s1 := database.Session{ + Key: "some-session-key-1", + UserID: u.ID, + ExpiresAt: time.Now().Add(time.Hour * 10 * 24), + } + testutils.MustExec(t, testutils.DB.Save(&s1), "preparing user session 1") + + s2 := &database.Session{ + Key: "some-session-key-2", + UserID: u.ID, + ExpiresAt: time.Now().Add(time.Hour * 10 * 24), + } + testutils.MustExec(t, testutils.DB.Save(&s2), "preparing user session 2") + + anotherUser := testutils.SetupUserData() + testutils.MustExec(t, testutils.DB.Save(&database.Session{ + Key: "some-session-key-3", + UserID: anotherUser.ID, + ExpiresAt: time.Now().Add(time.Hour * 10 * 24), + }), "preparing anotherUser session 1") + + // Execute + dat := url.Values{} + dat.Set("token", "MivFxYiSMMA4An9dP24DNQ==") + dat.Set("password", "newpassword") + dat.Set("password_confirmation", "newpassword") + req := testutils.MakeFormReq(server.URL, "PATCH", "/password-reset", dat) + + res := testutils.HTTPDo(t, req) + + // Test + assert.StatusCodeEquals(t, res, http.StatusFound, "Status code mismatch") + + var resetToken, verificationToken database.Token + var account database.Account + testutils.MustExec(t, testutils.DB.Where("value = ?", "MivFxYiSMMA4An9dP24DNQ==").First(&resetToken), "finding reset token") + testutils.MustExec(t, testutils.DB.Where("value = ?", "somerandomvalue").First(&verificationToken), "finding reset token") + testutils.MustExec(t, testutils.DB.Where("id = ?", a.ID).First(&account), "finding account") + + assert.NotEqual(t, resetToken.UsedAt, nil, "reset_token UsedAt mismatch") + passwordErr := bcrypt.CompareHashAndPassword([]byte(account.Password.String), []byte("newpassword")) + assert.Equal(t, passwordErr, nil, "Password mismatch") + assert.Equal(t, verificationToken.UsedAt, (*time.Time)(nil), "verificationToken UsedAt mismatch") + + var s1Count, s2Count int + testutils.MustExec(t, testutils.DB.Model(&database.Session{}).Where("id = ?", s1.ID).Count(&s1Count), "counting s1") + testutils.MustExec(t, testutils.DB.Model(&database.Session{}).Where("id = ?", s2.ID).Count(&s2Count), "counting s2") + + assert.Equal(t, s1Count, 0, "s1 should have been deleted") + assert.Equal(t, s2Count, 0, "s2 should have been deleted") + + var userSessionCount, anotherUserSessionCount int + testutils.MustExec(t, testutils.DB.Model(&database.Session{}).Where("user_id = ?", u.ID).Count(&userSessionCount), "counting user session") + testutils.MustExec(t, testutils.DB.Model(&database.Session{}).Where("user_id = ?", anotherUser.ID).Count(&anotherUserSessionCount), "counting anotherUser session") + + assert.Equal(t, userSessionCount, 0, "should have deleted a user session") + assert.Equal(t, anotherUserSessionCount, 1, "anotherUser session count mismatch") + }) + + t.Run("nonexistent token", func(t *testing.T) { + defer testutils.ClearData(testutils.DB) + + // Setup + server := MustNewServer(t, &app.App{ + Clock: clock.NewMock(), + Config: config.Config{ + PageTemplateDir: "../views", + }, + }) + defer server.Close() + + u := testutils.SetupUserData() + a := testutils.SetupAccountData(u, "alice@example.com", "somepassword") + tok := database.Token{ + UserID: u.ID, + Value: "MivFxYiSMMA4An9dP24DNQ==", + Type: database.TokenTypeResetPassword, + } + testutils.MustExec(t, testutils.DB.Save(&tok), "preparing token") + + dat := url.Values{} + dat.Set("token", "-ApMnyvpg59uOU5b-Kf5uQ==") + dat.Set("password", "oldpassword") + dat.Set("password_confirmation", "oldpassword") + req := testutils.MakeFormReq(server.URL, "PATCH", "/password-reset", dat) + + // Execute + res := testutils.HTTPDo(t, req) + + // Test + assert.StatusCodeEquals(t, res, http.StatusBadRequest, "Status code mismatch") + + var resetToken database.Token + var account database.Account + testutils.MustExec(t, testutils.DB.Where("value = ?", "MivFxYiSMMA4An9dP24DNQ==").First(&resetToken), "finding reset token") + testutils.MustExec(t, testutils.DB.Where("id = ?", a.ID).First(&account), "finding account") + + assert.Equal(t, a.Password, account.Password, "password should not have been updated") + assert.Equal(t, a.Password, account.Password, "password should not have been updated") + assert.Equal(t, resetToken.UsedAt, (*time.Time)(nil), "used_at should be nil") + }) + + t.Run("expired token", func(t *testing.T) { + defer testutils.ClearData(testutils.DB) + + // Setup + server := MustNewServer(t, &app.App{ + Clock: clock.NewMock(), + Config: config.Config{ + PageTemplateDir: "../views", + }, + }) + defer server.Close() + + u := testutils.SetupUserData() + a := testutils.SetupAccountData(u, "alice@example.com", "somepassword") + tok := database.Token{ + UserID: u.ID, + Value: "MivFxYiSMMA4An9dP24DNQ==", + Type: database.TokenTypeResetPassword, + } + testutils.MustExec(t, testutils.DB.Save(&tok), "preparing token") + testutils.MustExec(t, testutils.DB.Model(&tok).Update("created_at", time.Now().Add(time.Minute*-11)), "Failed to prepare reset_token created_at") + + dat := url.Values{} + dat.Set("token", "MivFxYiSMMA4An9dP24DNQ==") + dat.Set("password", "oldpassword") + dat.Set("password_confirmation", "oldpassword") + req := testutils.MakeFormReq(server.URL, "PATCH", "/password-reset", dat) + + // Execute + res := testutils.HTTPDo(t, req) + + // Test + assert.StatusCodeEquals(t, res, http.StatusGone, "Status code mismatch") + + var resetToken database.Token + var account database.Account + testutils.MustExec(t, testutils.DB.Where("value = ?", "MivFxYiSMMA4An9dP24DNQ==").First(&resetToken), "failed to find reset_token") + testutils.MustExec(t, testutils.DB.Where("id = ?", a.ID).First(&account), "failed to find account") + assert.Equal(t, a.Password, account.Password, "password should not have been updated") + assert.Equal(t, resetToken.UsedAt, (*time.Time)(nil), "used_at should be nil") + }) + + t.Run("used token", func(t *testing.T) { + defer testutils.ClearData(testutils.DB) + + // Setup + server := MustNewServer(t, &app.App{ + Clock: clock.NewMock(), + Config: config.Config{ + PageTemplateDir: "../views", + }, + }) + defer server.Close() + + u := testutils.SetupUserData() + a := testutils.SetupAccountData(u, "alice@example.com", "somepassword") + + usedAt := time.Now().Add(time.Hour * -11).UTC() + tok := database.Token{ + UserID: u.ID, + Value: "MivFxYiSMMA4An9dP24DNQ==", + Type: database.TokenTypeResetPassword, + UsedAt: &usedAt, + } + testutils.MustExec(t, testutils.DB.Save(&tok), "preparing token") + testutils.MustExec(t, testutils.DB.Model(&tok).Update("created_at", time.Now().Add(time.Minute*-11)), "Failed to prepare reset_token created_at") + + dat := url.Values{} + dat.Set("token", "MivFxYiSMMA4An9dP24DNQ==") + dat.Set("password", "oldpassword") + dat.Set("password_confirmation", "oldpassword") + req := testutils.MakeFormReq(server.URL, "PATCH", "/password-reset", dat) + + // Execute + res := testutils.HTTPDo(t, req) + + // Test + assert.StatusCodeEquals(t, res, http.StatusBadRequest, "Status code mismatch") + + var resetToken database.Token + var account database.Account + testutils.MustExec(t, testutils.DB.Where("value = ?", "MivFxYiSMMA4An9dP24DNQ==").First(&resetToken), "failed to find reset_token") + testutils.MustExec(t, testutils.DB.Where("id = ?", a.ID).First(&account), "failed to find account") + assert.Equal(t, a.Password, account.Password, "password should not have been updated") + + if resetToken.UsedAt.Year() != usedAt.Year() || + resetToken.UsedAt.Month() != usedAt.Month() || + resetToken.UsedAt.Day() != usedAt.Day() || + resetToken.UsedAt.Hour() != usedAt.Hour() || + resetToken.UsedAt.Minute() != usedAt.Minute() || + resetToken.UsedAt.Second() != usedAt.Second() { + t.Errorf("used_at should be %+v but got: %+v", usedAt, resetToken.UsedAt) + } + }) + + t.Run("using wrong type token: email_verification", func(t *testing.T) { + defer testutils.ClearData(testutils.DB) + + // Setup + server := MustNewServer(t, &app.App{ + Clock: clock.NewMock(), + Config: config.Config{ + PageTemplateDir: "../views", + }, + }) + defer server.Close() + + u := testutils.SetupUserData() + a := testutils.SetupAccountData(u, "alice@example.com", "somepassword") + tok := database.Token{ + UserID: u.ID, + Value: "MivFxYiSMMA4An9dP24DNQ==", + Type: database.TokenTypeEmailVerification, + } + testutils.MustExec(t, testutils.DB.Save(&tok), "Failed to prepare reset_token") + testutils.MustExec(t, testutils.DB.Model(&tok).Update("created_at", time.Now().Add(time.Minute*-11)), "Failed to prepare reset_token created_at") + + dat := url.Values{} + dat.Set("token", "MivFxYiSMMA4An9dP24DNQ==") + dat.Set("password", "oldpassword") + dat.Set("password_confirmation", "oldpassword") + req := testutils.MakeFormReq(server.URL, "PATCH", "/password-reset", dat) + + // Execute + res := testutils.HTTPDo(t, req) + + // Test + assert.StatusCodeEquals(t, res, http.StatusBadRequest, "Status code mismatch") + + var resetToken database.Token + var account database.Account + testutils.MustExec(t, testutils.DB.Where("value = ?", "MivFxYiSMMA4An9dP24DNQ==").First(&resetToken), "failed to find reset_token") + testutils.MustExec(t, testutils.DB.Where("id = ?", a.ID).First(&account), "failed to find account") + + assert.Equal(t, a.Password, account.Password, "password should not have been updated") + assert.Equal(t, resetToken.UsedAt, (*time.Time)(nil), "used_at should be nil") + }) +} + +func TestCreateResetToken(t *testing.T) { + t.Run("success", func(t *testing.T) { + defer testutils.ClearData(testutils.DB) + + // Setup + server := MustNewServer(t, &app.App{ + Clock: clock.NewMock(), + Config: config.Config{ + PageTemplateDir: "../views", + }, + }) + defer server.Close() + + u := testutils.SetupUserData() + testutils.SetupAccountData(u, "alice@example.com", "somepassword") + + // Execute + dat := url.Values{} + dat.Set("email", "alice@example.com") + req := testutils.MakeFormReq(server.URL, "POST", "/reset-token", dat) + + res := testutils.HTTPDo(t, req) + + // Test + assert.StatusCodeEquals(t, res, http.StatusFound, "Status code mismtach") + + var tokenCount int + testutils.MustExec(t, testutils.DB.Model(&database.Token{}).Count(&tokenCount), "counting tokens") + + var resetToken database.Token + testutils.MustExec(t, testutils.DB.Where("user_id = ? AND type = ?", u.ID, database.TokenTypeResetPassword).First(&resetToken), "finding reset token") + + assert.Equal(t, tokenCount, 1, "reset_token count mismatch") + assert.NotEqual(t, resetToken.Value, nil, "reset_token value mismatch") + assert.Equal(t, resetToken.UsedAt, (*time.Time)(nil), "reset_token UsedAt mismatch") + }) + + t.Run("nonexistent email", func(t *testing.T) { + defer testutils.ClearData(testutils.DB) + + // Setup + server := MustNewServer(t, &app.App{ + Clock: clock.NewMock(), + Config: config.Config{ + PageTemplateDir: "../views", + }, + }) + defer server.Close() + + u := testutils.SetupUserData() + testutils.SetupAccountData(u, "alice@example.com", "somepassword") + + // Execute + dat := url.Values{} + dat.Set("email", "bob@example.com") + req := testutils.MakeFormReq(server.URL, "POST", "/reset-token", dat) + + res := testutils.HTTPDo(t, req) + + // Test + assert.StatusCodeEquals(t, res, http.StatusOK, "Status code mismtach") + + var tokenCount int + testutils.MustExec(t, testutils.DB.Model(&database.Token{}).Count(&tokenCount), "counting tokens") + assert.Equal(t, tokenCount, 0, "reset_token count mismatch") + }) +} + +func TestUpdatePassword(t *testing.T) { + t.Run("success", func(t *testing.T) { + defer testutils.ClearData(testutils.DB) + + // Setup + server := MustNewServer(t, &app.App{ + Clock: clock.NewMock(), + Config: config.Config{ + PageTemplateDir: "../views", + }, + }) + defer server.Close() + + user := testutils.SetupUserData() + testutils.SetupAccountData(user, "alice@example.com", "oldpassword") + + // Execute + dat := url.Values{} + dat.Set("old_password", "oldpassword") + dat.Set("new_password", "newpassword") + dat.Set("new_password_confirmation", "newpassword") + req := testutils.MakeFormReq(server.URL, "PATCH", "/account/password", dat) + + res := testutils.HTTPAuthDo(t, req, user) + + // Test + assert.StatusCodeEquals(t, res, http.StatusFound, "Status code mismsatch") + + var account database.Account + testutils.MustExec(t, testutils.DB.Where("user_id = ?", user.ID).First(&account), "finding account") + + passwordErr := bcrypt.CompareHashAndPassword([]byte(account.Password.String), []byte("newpassword")) + assert.Equal(t, passwordErr, nil, "Password mismatch") + }) + + t.Run("old password mismatch", func(t *testing.T) { + defer testutils.ClearData(testutils.DB) + // Setup + server := MustNewServer(t, &app.App{ + Clock: clock.NewMock(), + Config: config.Config{ + PageTemplateDir: "../views", + }, + }) + defer server.Close() + + u := testutils.SetupUserData() + a := testutils.SetupAccountData(u, "alice@example.com", "oldpassword") + + // Execute + dat := url.Values{} + dat.Set("old_password", "randompassword") + dat.Set("new_password", "newpassword") + dat.Set("new_password_confirmation", "newpassword") + req := testutils.MakeFormReq(server.URL, "PATCH", "/account/password", dat) + + res := testutils.HTTPAuthDo(t, req, u) + + // Test + assert.StatusCodeEquals(t, res, http.StatusUnauthorized, "Status code mismsatch") + + var account database.Account + testutils.MustExec(t, testutils.DB.Where("user_id = ?", u.ID).First(&account), "finding account") + assert.Equal(t, a.Password.String, account.Password.String, "password should not have been updated") + }) + + t.Run("password too short", func(t *testing.T) { + defer testutils.ClearData(testutils.DB) + + // Setup + server := MustNewServer(t, &app.App{ + Clock: clock.NewMock(), + Config: config.Config{ + PageTemplateDir: "../views", + }, + }) + defer server.Close() + + u := testutils.SetupUserData() + a := testutils.SetupAccountData(u, "alice@example.com", "oldpassword") + + // Execute + dat := url.Values{} + dat.Set("old_password", "oldpassword") + dat.Set("new_password", "a") + dat.Set("new_password_confirmation", "a") + req := testutils.MakeFormReq(server.URL, "PATCH", "/account/password", dat) + + res := testutils.HTTPAuthDo(t, req, u) + + // Test + assert.StatusCodeEquals(t, res, http.StatusBadRequest, "Status code mismsatch") + + var account database.Account + testutils.MustExec(t, testutils.DB.Where("user_id = ?", u.ID).First(&account), "finding account") + assert.Equal(t, a.Password.String, account.Password.String, "password should not have been updated") + }) + + t.Run("password confirmation mismatch", func(t *testing.T) { + defer testutils.ClearData(testutils.DB) + + // Setup + server := MustNewServer(t, &app.App{ + Clock: clock.NewMock(), + Config: config.Config{ + PageTemplateDir: "../views", + }, + }) + defer server.Close() + + u := testutils.SetupUserData() + a := testutils.SetupAccountData(u, "alice@example.com", "oldpassword") + + // Execute + dat := url.Values{} + dat.Set("old_password", "oldpassword") + dat.Set("new_password", "newpassword1") + dat.Set("new_password_confirmation", "newpassword2") + req := testutils.MakeFormReq(server.URL, "PATCH", "/account/password", dat) + + res := testutils.HTTPAuthDo(t, req, u) + + // Test + assert.StatusCodeEquals(t, res, http.StatusBadRequest, "Status code mismsatch") + + var account database.Account + testutils.MustExec(t, testutils.DB.Where("user_id = ?", u.ID).First(&account), "finding account") + assert.Equal(t, a.Password.String, account.Password.String, "password should not have been updated") + }) +} + +func TestUpdateEmail(t *testing.T) { + t.Run("success", func(t *testing.T) { + defer testutils.ClearData(testutils.DB) + + // Setup + server := MustNewServer(t, &app.App{ + Clock: clock.NewMock(), + Config: config.Config{ + PageTemplateDir: "../views", + }, + }) + defer server.Close() + + u := testutils.SetupUserData() + a := testutils.SetupAccountData(u, "alice@example.com", "pass1234") + a.EmailVerified = true + testutils.MustExec(t, testutils.DB.Save(&a), "updating email_verified") + + // Execute + dat := url.Values{} + dat.Set("email", "alice-new@example.com") + dat.Set("password", "pass1234") + req := testutils.MakeFormReq(server.URL, "PATCH", "/account/profile", dat) + + res := testutils.HTTPAuthDo(t, req, u) + + // Test + assert.StatusCodeEquals(t, res, http.StatusFound, "Status code mismatch") + + var user database.User + var account database.Account + testutils.MustExec(t, testutils.DB.Where("id = ?", u.ID).First(&user), "finding user") + testutils.MustExec(t, testutils.DB.Where("user_id = ?", u.ID).First(&account), "finding account") + + assert.Equal(t, account.Email.String, "alice-new@example.com", "email mismatch") + assert.Equal(t, account.EmailVerified, false, "EmailVerified mismatch") + }) + + t.Run("password mismatch", func(t *testing.T) { + defer testutils.ClearData(testutils.DB) + + // Setup + server := MustNewServer(t, &app.App{ + Clock: clock.NewMock(), + Config: config.Config{ + PageTemplateDir: "../views", + }, + }) + defer server.Close() + + u := testutils.SetupUserData() + a := testutils.SetupAccountData(u, "alice@example.com", "pass1234") + a.EmailVerified = true + testutils.MustExec(t, testutils.DB.Save(&a), "updating email_verified") + + // Execute + dat := url.Values{} + dat.Set("email", "alice-new@example.com") + dat.Set("password", "wrongpassword") + req := testutils.MakeFormReq(server.URL, "PATCH", "/account/profile", dat) + + res := testutils.HTTPAuthDo(t, req, u) + + // Test + assert.StatusCodeEquals(t, res, http.StatusUnauthorized, "Status code mismsatch") + + var user database.User + var account database.Account + testutils.MustExec(t, testutils.DB.Where("id = ?", u.ID).First(&user), "finding user") + testutils.MustExec(t, testutils.DB.Where("user_id = ?", u.ID).First(&account), "finding account") + + assert.Equal(t, account.Email.String, "alice@example.com", "email mismatch") + assert.Equal(t, account.EmailVerified, true, "EmailVerified mismatch") + }) +} + +func TestVerifyEmail(t *testing.T) { + t.Run("success", func(t *testing.T) { + defer testutils.ClearData(testutils.DB) + + // Setup + server := MustNewServer(t, &app.App{ + Clock: clock.NewMock(), + Config: config.Config{ + PageTemplateDir: "../views", + }, + }) + defer server.Close() + + user := testutils.SetupUserData() + testutils.SetupAccountData(user, "alice@example.com", "pass1234") + tok := database.Token{ + UserID: user.ID, + Type: database.TokenTypeEmailVerification, + Value: "someTokenValue", + } + testutils.MustExec(t, testutils.DB.Save(&tok), "preparing token") + + // Execute + req := testutils.MakeReq(server.URL, "GET", fmt.Sprintf("/verify-email/%s", "someTokenValue"), "") + res := testutils.HTTPAuthDo(t, req, user) + + // Test + assert.StatusCodeEquals(t, res, http.StatusFound, "Status code mismatch") + + var account database.Account + var token database.Token + var tokenCount int + testutils.MustExec(t, testutils.DB.Where("user_id = ?", user.ID).First(&account), "finding account") + testutils.MustExec(t, testutils.DB.Where("user_id = ? AND type = ?", user.ID, database.TokenTypeEmailVerification).First(&token), "finding token") + testutils.MustExec(t, testutils.DB.Model(&database.Token{}).Count(&tokenCount), "counting token") + + assert.Equal(t, account.EmailVerified, true, "email_verified mismatch") + assert.NotEqual(t, token.Value, "", "token value should not have been updated") + assert.Equal(t, tokenCount, 1, "token count mismatch") + assert.NotEqual(t, token.UsedAt, (*time.Time)(nil), "token should have been used") + }) + + t.Run("used token", func(t *testing.T) { + defer testutils.ClearData(testutils.DB) + + // Setup + server := MustNewServer(t, &app.App{ + Clock: clock.NewMock(), + Config: config.Config{ + PageTemplateDir: "../views", + }, + }) + defer server.Close() + + user := testutils.SetupUserData() + testutils.SetupAccountData(user, "alice@example.com", "pass1234") + + usedAt := time.Now().Add(time.Hour * -11).UTC() + tok := database.Token{ + UserID: user.ID, + Type: database.TokenTypeEmailVerification, + Value: "someTokenValue", + UsedAt: &usedAt, + } + testutils.MustExec(t, testutils.DB.Save(&tok), "preparing token") + + // Execute + req := testutils.MakeReq(server.URL, "GET", fmt.Sprintf("/verify-email/%s", "someTokenValue"), "") + res := testutils.HTTPAuthDo(t, req, user) + + // Test + assert.StatusCodeEquals(t, res, http.StatusBadRequest, "") + + var account database.Account + var token database.Token + var tokenCount int + testutils.MustExec(t, testutils.DB.Where("user_id = ?", user.ID).First(&account), "finding account") + testutils.MustExec(t, testutils.DB.Where("user_id = ? AND type = ?", user.ID, database.TokenTypeEmailVerification).First(&token), "finding token") + testutils.MustExec(t, testutils.DB.Model(&database.Token{}).Count(&tokenCount), "counting token") + + assert.Equal(t, account.EmailVerified, false, "email_verified mismatch") + assert.NotEqual(t, token.UsedAt, nil, "token used_at mismatch") + assert.Equal(t, tokenCount, 1, "token count mismatch") + assert.NotEqual(t, token.UsedAt, (*time.Time)(nil), "token should have been used") + }) + + t.Run("expired token", func(t *testing.T) { + defer testutils.ClearData(testutils.DB) + + // Setup + server := MustNewServer(t, &app.App{ + Clock: clock.NewMock(), + Config: config.Config{ + PageTemplateDir: "../views", + }, + }) + defer server.Close() + + user := testutils.SetupUserData() + testutils.SetupAccountData(user, "alice@example.com", "pass1234") + + tok := database.Token{ + UserID: user.ID, + Type: database.TokenTypeEmailVerification, + Value: "someTokenValue", + } + testutils.MustExec(t, testutils.DB.Save(&tok), "preparing token") + testutils.MustExec(t, testutils.DB.Model(&tok).Update("created_at", time.Now().Add(time.Minute*-31)), "Failed to prepare token created_at") + + // Execute + req := testutils.MakeReq(server.URL, "GET", fmt.Sprintf("/verify-email/%s", "someTokenValue"), "") + res := testutils.HTTPAuthDo(t, req, user) + + // Test + assert.StatusCodeEquals(t, res, http.StatusGone, "") + + var account database.Account + var token database.Token + var tokenCount int + testutils.MustExec(t, testutils.DB.Where("user_id = ?", user.ID).First(&account), "finding account") + testutils.MustExec(t, testutils.DB.Where("user_id = ? AND type = ?", user.ID, database.TokenTypeEmailVerification).First(&token), "finding token") + testutils.MustExec(t, testutils.DB.Model(&database.Token{}).Count(&tokenCount), "counting token") + + assert.Equal(t, account.EmailVerified, false, "email_verified mismatch") + assert.Equal(t, tokenCount, 1, "token count mismatch") + assert.Equal(t, token.UsedAt, (*time.Time)(nil), "token should have not been used") + }) + + t.Run("already verified", func(t *testing.T) { + defer testutils.ClearData(testutils.DB) + + // Setup + server := MustNewServer(t, &app.App{ + Clock: clock.NewMock(), + Config: config.Config{ + PageTemplateDir: "../views", + }, + }) + defer server.Close() + + user := testutils.SetupUserData() + a := testutils.SetupAccountData(user, "alice@example.com", "oldpass1234") + a.EmailVerified = true + testutils.MustExec(t, testutils.DB.Save(&a), "preparing account") + + tok := database.Token{ + UserID: user.ID, + Type: database.TokenTypeEmailVerification, + Value: "someTokenValue", + } + testutils.MustExec(t, testutils.DB.Save(&tok), "preparing token") + + // Execute + req := testutils.MakeReq(server.URL, "GET", fmt.Sprintf("/verify-email/%s", "someTokenValue"), "") + res := testutils.HTTPAuthDo(t, req, user) + + // Test + assert.StatusCodeEquals(t, res, http.StatusConflict, "") + + var account database.Account + var token database.Token + var tokenCount int + testutils.MustExec(t, testutils.DB.Where("user_id = ?", user.ID).First(&account), "finding account") + testutils.MustExec(t, testutils.DB.Where("user_id = ? AND type = ?", user.ID, database.TokenTypeEmailVerification).First(&token), "finding token") + testutils.MustExec(t, testutils.DB.Model(&database.Token{}).Count(&tokenCount), "counting token") + + assert.Equal(t, account.EmailVerified, true, "email_verified mismatch") + assert.Equal(t, tokenCount, 1, "token count mismatch") + assert.Equal(t, token.UsedAt, (*time.Time)(nil), "token should have not been used") + }) +} + +func TestCreateVerificationToken(t *testing.T) { + t.Run("success", func(t *testing.T) { + defer testutils.ClearData(testutils.DB) + + // Setup + emailBackend := testutils.MockEmailbackendImplementation{} + server := MustNewServer(t, &app.App{ + Clock: clock.NewMock(), + Config: config.Config{ + PageTemplateDir: "../views", + }, + EmailBackend: &emailBackend, + }) + defer server.Close() + + user := testutils.SetupUserData() + testutils.SetupAccountData(user, "alice@example.com", "pass1234") + + // Execute + req := testutils.MakeReq(server.URL, "POST", "/verification-token", "") + res := testutils.HTTPAuthDo(t, req, user) + + // Test + assert.StatusCodeEquals(t, res, http.StatusFound, "status code mismatch") + + var account database.Account + var token database.Token + var tokenCount int + testutils.MustExec(t, testutils.DB.Where("user_id = ?", user.ID).First(&account), "finding account") + testutils.MustExec(t, testutils.DB.Where("user_id = ? AND type = ?", user.ID, database.TokenTypeEmailVerification).First(&token), "finding token") + testutils.MustExec(t, testutils.DB.Model(&database.Token{}).Count(&tokenCount), "counting token") + + assert.Equal(t, account.EmailVerified, false, "email_verified should not have been updated") + assert.NotEqual(t, token.Value, "", "token Value mismatch") + assert.Equal(t, tokenCount, 1, "token count mismatch") + assert.Equal(t, token.UsedAt, (*time.Time)(nil), "token UsedAt mismatch") + assert.Equal(t, len(emailBackend.Emails), 1, "email queue count mismatch") + }) + + t.Run("already verified", func(t *testing.T) { + defer testutils.ClearData(testutils.DB) + // Setup + server := MustNewServer(t, &app.App{ + Clock: clock.NewMock(), + Config: config.Config{ + PageTemplateDir: "../views", + }, + }) + defer server.Close() + + user := testutils.SetupUserData() + a := testutils.SetupAccountData(user, "alice@example.com", "pass1234") + a.EmailVerified = true + testutils.MustExec(t, testutils.DB.Save(&a), "preparing account") + + // Execute + req := testutils.MakeReq(server.URL, "POST", "/verification-token", "") + res := testutils.HTTPAuthDo(t, req, user) + + // Test + assert.StatusCodeEquals(t, res, http.StatusConflict, "Status code mismatch") + + var account database.Account + var tokenCount int + testutils.MustExec(t, testutils.DB.Where("user_id = ?", user.ID).First(&account), "finding account") + testutils.MustExec(t, testutils.DB.Model(&database.Token{}).Count(&tokenCount), "counting token") + + assert.Equal(t, account.EmailVerified, true, "email_verified should not have been updated") + assert.Equal(t, tokenCount, 0, "token count mismatch") + }) +} diff --git a/pkg/server/database/errors.go b/pkg/server/database/errors.go new file mode 100644 index 00000000..e142a83d --- /dev/null +++ b/pkg/server/database/errors.go @@ -0,0 +1,12 @@ +package database + +import ( + "github.com/pkg/errors" +) + +type modelError string + +var ( + // ErrNotFound an error that indicates that the given resource is not found + ErrNotFound error = errors.New("not found") +) diff --git a/pkg/server/handlers/main_test.go b/pkg/server/helpers/url.go similarity index 70% rename from pkg/server/handlers/main_test.go rename to pkg/server/helpers/url.go index 550c919e..e136b230 100644 --- a/pkg/server/handlers/main_test.go +++ b/pkg/server/helpers/url.go @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd +/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd * * This file is part of Dnote. * @@ -16,20 +16,19 @@ * along with Dnote. If not, see . */ -package handlers +package helpers import ( - "os" - "testing" - - "github.com/dnote/dnote/pkg/server/testutils" + "fmt" + "net/url" ) -func TestMain(m *testing.M) { - testutils.InitTestDB() +// GetPath returns a path optionally suffixed by query string +func GetPath(path string, query *url.Values) string { + if query == nil { + return path + } - code := m.Run() - testutils.ClearData(testutils.DB) - - os.Exit(code) + q := query.Encode() + return fmt.Sprintf("%s?%s", path, q) } diff --git a/pkg/server/helpers/url_test.go b/pkg/server/helpers/url_test.go new file mode 100644 index 00000000..14752e93 --- /dev/null +++ b/pkg/server/helpers/url_test.go @@ -0,0 +1,29 @@ +package helpers + +import ( + "net/url" + "testing" + + "github.com/dnote/dnote/pkg/assert" +) + +func TestGetPath(t *testing.T) { + t.Run("without query", func(t *testing.T) { + // execute + got := GetPath("/some-path", nil) + + // test + assert.Equal(t, got, "/some-path", "got mismatch") + }) + + t.Run("with query", func(t *testing.T) { + // execute + q := url.Values{} + q.Set("foo", "bar") + q.Set("baz", "/quz") + got := GetPath("/some-path", &q) + + // test + assert.Equal(t, got, "/some-path?baz=%2Fquz&foo=bar", "got mismatch") + }) +} diff --git a/pkg/server/helpers/helpers.go b/pkg/server/helpers/uuid.go similarity index 100% rename from pkg/server/helpers/helpers.go rename to pkg/server/helpers/uuid.go diff --git a/pkg/server/job/job.go b/pkg/server/job/job.go index 45990a49..6f069ad6 100644 --- a/pkg/server/job/job.go +++ b/pkg/server/job/job.go @@ -23,8 +23,6 @@ import ( "github.com/dnote/dnote/pkg/clock" "github.com/dnote/dnote/pkg/server/config" - "github.com/dnote/dnote/pkg/server/job/remind" - "github.com/dnote/dnote/pkg/server/log" "github.com/dnote/dnote/pkg/server/mailer" "github.com/jinzhu/gorm" "github.com/pkg/errors" @@ -102,7 +100,6 @@ func scheduleJob(c *cron.Cron, spec string, cmd func()) { func (r *Runner) schedule(ch chan error) { // Schedule jobs cr := cron.New() - scheduleJob(cr, "0 8 * * *", func() { r.RemindNoRecentNotes() }) cr.Start() ch <- nil @@ -128,26 +125,3 @@ func (r *Runner) Do() error { return nil } - -// RemindNoRecentNotes remind users if no notes have been added recently -func (r *Runner) RemindNoRecentNotes() { - c := remind.Context{ - DB: r.DB, - Clock: r.Clock, - EmailTmpl: r.EmailTmpl, - EmailBackend: r.EmailBackend, - Config: r.Config, - } - - result, err := remind.DoInactive(c) - m := log.WithFields(log.Fields{ - "success_count": result.SuccessCount, - "failed_user_ids": result.FailedUserIDs, - }) - - if err == nil { - m.Info("successfully processed no recent note reminder job") - } else { - m.ErrorWrap(err, "error processing no recent note reminder job") - } -} diff --git a/pkg/server/job/remind/inactive.go b/pkg/server/job/remind/inactive.go deleted file mode 100644 index d0e9de01..00000000 --- a/pkg/server/job/remind/inactive.go +++ /dev/null @@ -1,210 +0,0 @@ -/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd - * - * This file is part of Dnote. - * - * Dnote is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Dnote is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Dnote. If not, see . - */ - -package remind - -import ( - "github.com/dnote/dnote/pkg/clock" - "github.com/dnote/dnote/pkg/server/app" - "github.com/dnote/dnote/pkg/server/config" - "github.com/dnote/dnote/pkg/server/database" - "github.com/dnote/dnote/pkg/server/log" - "github.com/dnote/dnote/pkg/server/mailer" - "github.com/jinzhu/gorm" - "github.com/pkg/errors" -) - -// Context holds data that repetition job needs in order to perform -type Context struct { - DB *gorm.DB - Clock clock.Clock - EmailTmpl mailer.Templates - EmailBackend mailer.Backend - Config config.Config -} - -type inactiveUserInfo struct { - userID int - email string - sampleNoteUUID string -} - -func (c *Context) sampleUserNote(userID int) (database.Note, error) { - var ret database.Note - // FIXME: ordering by random() requires a sequential scan on the whole table and does not scale - if err := c.DB.Where("user_id = ?", userID).Order("random() DESC").First(&ret).Error; err != nil { - return ret, errors.Wrap(err, "getting a random note") - } - - return ret, nil -} - -func (c *Context) getInactiveUserInfo() ([]inactiveUserInfo, error) { - ret := []inactiveUserInfo{} - - threshold := c.Clock.Now().AddDate(0, 0, -14).Unix() - - rows, err := c.DB.Raw(` -SELECT - notes.user_id AS user_id, - accounts.email, - SUM( - CASE - WHEN notes.created_at > to_timestamp(?) THEN 1 - ELSE 0 - END - ) AS recent_note_count, - COUNT(*) AS total_note_count -FROM notes -INNER JOIN accounts ON accounts.user_id = notes.user_id -WHERE accounts.email IS NOT NULL AND accounts.email_verified IS TRUE -GROUP BY notes.user_id, accounts.email`, threshold).Rows() - if err != nil { - return ret, errors.Wrap(err, "executing note count SQL query") - } - defer rows.Close() - for rows.Next() { - var userID, recentNoteCount, totalNoteCount int - var email string - if err := rows.Scan(&userID, &email, &recentNoteCount, &totalNoteCount); err != nil { - return nil, errors.Wrap(err, "scanning a row") - } - - if recentNoteCount == 0 && totalNoteCount > 0 { - note, err := c.sampleUserNote(userID) - if err != nil { - return nil, errors.Wrap(err, "sampling user note") - } - - ret = append(ret, inactiveUserInfo{ - userID: userID, - email: email, - sampleNoteUUID: note.UUID, - }) - } - } - - return ret, nil -} - -func (c *Context) canNotify(info inactiveUserInfo) (bool, error) { - var pref database.EmailPreference - if err := c.DB.Where("user_id = ?", info.userID).First(&pref).Error; err != nil { - return false, errors.Wrap(err, "getting email preference") - } - - if !pref.InactiveReminder { - return false, nil - } - - var notif database.Notification - conn := c.DB.Where("user_id = ? AND type = ?", info.userID, mailer.EmailTypeInactiveReminder).Order("created_at DESC").First(¬if) - - if conn.RecordNotFound() { - return true, nil - } else if err := conn.Error; err != nil { - return false, errors.Wrap(err, "checking cooldown") - } - - t := c.Clock.Now().AddDate(0, 0, -14) - if notif.CreatedAt.Before(t) { - return true, nil - } - - return false, nil -} - -func (c *Context) process(info inactiveUserInfo) error { - ok, err := c.canNotify(info) - if err != nil { - return errors.Wrap(err, "checking if user can be notified") - } - if !ok { - return nil - } - - sender, err := app.GetSenderEmail(c.Config, "noreply@getdnote.com") - if err != nil { - return errors.Wrap(err, "getting sender email") - } - - tok, err := mailer.GetToken(c.DB, info.userID, database.TokenTypeEmailPreference) - if err != nil { - return errors.Wrap(err, "getting email token") - } - - tmplData := mailer.InactiveReminderTmplData{ - WebURL: c.Config.WebURL, - SampleNoteUUID: info.sampleNoteUUID, - Token: tok.Value, - } - body, err := c.EmailTmpl.Execute(mailer.EmailTypeInactiveReminder, mailer.EmailKindText, tmplData) - if err != nil { - return errors.Wrap(err, "executing inactive email template") - } - - if err := c.EmailBackend.Queue("Your Dnote stopped growing", sender, []string{info.email}, mailer.EmailKindText, body); err != nil { - return errors.Wrap(err, "queueing email") - } - - if err := c.DB.Create(&database.Notification{ - Type: mailer.EmailTypeInactiveReminder, - UserID: info.userID, - }).Error; err != nil { - return errors.Wrap(err, "creating notification") - } - - return nil -} - -// Result holds the result of the job -type Result struct { - SuccessCount int - FailedUserIDs []int -} - -// DoInactive sends reminder for users with no recent notes -func DoInactive(c Context) (Result, error) { - log.Info("performing reminder for no recent notes") - - result := Result{} - items, err := c.getInactiveUserInfo() - if err != nil { - return result, errors.Wrap(err, "getting inactive user information") - } - - log.WithFields(log.Fields{ - "user_count": len(items), - }).Info("counted inactive users") - - for _, item := range items { - err := c.process(item) - - if err == nil { - result.SuccessCount = result.SuccessCount + 1 - } else { - log.WithFields(log.Fields{ - "user_id": item.userID, - }).ErrorWrap(err, "Could not process no recent notes reminder") - - result.FailedUserIDs = append(result.FailedUserIDs, item.userID) - } - } - - return result, nil -} diff --git a/pkg/server/job/remind/inactive_test.go b/pkg/server/job/remind/inactive_test.go deleted file mode 100644 index d79095c5..00000000 --- a/pkg/server/job/remind/inactive_test.go +++ /dev/null @@ -1,194 +0,0 @@ -/* Copyright (C) 2019, 2020, 2021 Monomax Software Pty Ltd - * - * This file is part of Dnote. - * - * Dnote is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Dnote is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Dnote. If not, see . - */ - -package remind - -import ( - "os" - "sort" - "testing" - "time" - - "github.com/dnote/dnote/pkg/assert" - "github.com/dnote/dnote/pkg/clock" - "github.com/dnote/dnote/pkg/server/database" - "github.com/dnote/dnote/pkg/server/mailer" - "github.com/dnote/dnote/pkg/server/testutils" - "github.com/pkg/errors" -) - -func getTestContext(c clock.Clock, be *testutils.MockEmailbackendImplementation) Context { - emailTmplDir := os.Getenv("DNOTE_TEST_EMAIL_TEMPLATE_DIR") - - con := Context{ - DB: testutils.DB, - Clock: c, - EmailTmpl: mailer.NewTemplates(&emailTmplDir), - EmailBackend: be, - } - - return con -} - -func TestDoInactive(t *testing.T) { - defer testutils.ClearData(testutils.DB) - - t1 := time.Now() - - // u1 is an active user - u1 := testutils.SetupUserData() - a1 := testutils.SetupAccountData(u1, "alice@example.com", "pass1234") - testutils.MustExec(t, testutils.DB.Model(&a1).Update("email_verified", true), "setting email verified") - testutils.MustExec(t, testutils.DB.Save(&database.EmailPreference{UserID: u1.ID, InactiveReminder: true}), "preparing email preference") - - b1 := database.Book{ - UserID: u1.ID, - Label: "js", - } - testutils.MustExec(t, testutils.DB.Save(&b1), "preparing b1") - n1 := database.Note{ - BookUUID: b1.UUID, - UserID: u1.ID, - } - testutils.MustExec(t, testutils.DB.Save(&n1), "preparing n1") - - // u2 is an inactive user - u2 := testutils.SetupUserData() - a2 := testutils.SetupAccountData(u2, "bob@example.com", "pass1234") - testutils.MustExec(t, testutils.DB.Model(&a2).Update("email_verified", true), "setting email verified") - testutils.MustExec(t, testutils.DB.Save(&database.EmailPreference{UserID: u2.ID, InactiveReminder: true}), "preparing email preference") - - b2 := database.Book{ - UserID: u2.ID, - Label: "css", - } - testutils.MustExec(t, testutils.DB.Save(&b2), "preparing b2") - n2 := database.Note{ - UserID: u2.ID, - BookUUID: b2.UUID, - } - testutils.MustExec(t, testutils.DB.Save(&n2), "preparing n2") - testutils.MustExec(t, testutils.DB.Model(&n2).Update("created_at", t1.AddDate(0, 0, -15)), "preparing n2") - - // u3 is an inactive user with inactive alert email preference disabled - u3 := testutils.SetupUserData() - a3 := testutils.SetupAccountData(u3, "alice@example.com", "pass1234") - testutils.MustExec(t, testutils.DB.Model(&a3).Update("email_verified", true), "setting email verified") - emailPref3 := database.EmailPreference{UserID: u3.ID} - testutils.MustExec(t, testutils.DB.Save(&emailPref3), "preparing email preference") - testutils.MustExec(t, testutils.DB.Model(&emailPref3).Update(map[string]interface{}{"inactive_reminder": false}), "updating email preference") - - b3 := database.Book{ - UserID: u3.ID, - Label: "js", - } - testutils.MustExec(t, testutils.DB.Save(&b3), "preparing b3") - n3 := database.Note{ - BookUUID: b3.UUID, - UserID: u3.ID, - } - testutils.MustExec(t, testutils.DB.Save(&n3), "preparing n3") - testutils.MustExec(t, testutils.DB.Model(&n3).Update("created_at", t1.AddDate(0, 0, -15)), "preparing n3") - - c := clock.NewMock() - c.SetNow(t1) - be := &testutils.MockEmailbackendImplementation{} - - con := getTestContext(c, be) - if _, err := DoInactive(con); err != nil { - t.Fatal(errors.Wrap(err, "performing")) - } - - assert.Equalf(t, len(be.Emails), 1, "email queue count mismatch") - assert.DeepEqual(t, be.Emails[0].To, []string{a2.Email.String}, "email address mismatch") -} - -func TestDoInactive_Cooldown(t *testing.T) { - defer testutils.ClearData(testutils.DB) - - // setup sets up an inactive user - setup := func(t *testing.T, now time.Time, email string) database.User { - u := testutils.SetupUserData() - a := testutils.SetupAccountData(u, email, "pass1234") - testutils.MustExec(t, testutils.DB.Model(&a).Update("email_verified", true), "setting email verified") - testutils.MustExec(t, testutils.DB.Save(&database.EmailPreference{UserID: u.ID, InactiveReminder: true}), "preparing email preference") - - b := database.Book{ - UserID: u.ID, - Label: "css", - } - testutils.MustExec(t, testutils.DB.Save(&b), "preparing book") - n := database.Note{ - UserID: u.ID, - BookUUID: b.UUID, - } - testutils.MustExec(t, testutils.DB.Save(&n), "preparing note") - testutils.MustExec(t, testutils.DB.Model(&n).Update("created_at", now.AddDate(0, 0, -15)), "preparing note") - - return u - } - - // Set up - now := time.Now() - - setup(t, now, "alice@example.com") - - bob := setup(t, now, "bob@example.com") - bobNotif := database.Notification{Type: mailer.EmailTypeInactiveReminder, UserID: bob.ID} - testutils.MustExec(t, testutils.DB.Create(&bobNotif), "preparing inactive notification for bob") - testutils.MustExec(t, testutils.DB.Model(&bobNotif).Update("created_at", now.AddDate(0, 0, -7)), "preparing created_at for inactive notification for bob") - - chuck := setup(t, now, "chuck@example.com") - chuckNotif := database.Notification{Type: mailer.EmailTypeInactiveReminder, UserID: chuck.ID} - testutils.MustExec(t, testutils.DB.Create(&chuckNotif), "preparing inactive notification for chuck") - testutils.MustExec(t, testutils.DB.Model(&chuckNotif).Update("created_at", now.AddDate(0, 0, -15)), "preparing created_at for inactive notification for chuck") - - dan := setup(t, now, "dan@example.com") - danNotif1 := database.Notification{Type: mailer.EmailTypeInactiveReminder, UserID: dan.ID} - testutils.MustExec(t, testutils.DB.Create(&danNotif1), "preparing inactive notification 1 for dan") - testutils.MustExec(t, testutils.DB.Model(&danNotif1).Update("created_at", now.AddDate(0, 0, -10)), "preparing created_at for inactive notification for dan") - danNotif2 := database.Notification{Type: mailer.EmailTypeInactiveReminder, UserID: dan.ID} - testutils.MustExec(t, testutils.DB.Create(&danNotif2), "preparing inactive notification 2 for dan") - testutils.MustExec(t, testutils.DB.Model(&danNotif2).Update("created_at", now.AddDate(0, 0, -15)), "preparing created_at for inactive notification for dan") - - c := clock.NewMock() - c.SetNow(now) - be := &testutils.MockEmailbackendImplementation{} - - // Execute - con := getTestContext(c, be) - if _, err := DoInactive(con); err != nil { - t.Fatal(errors.Wrap(err, "performing")) - } - - // Test - assert.Equalf(t, len(be.Emails), 2, "email queue count mismatch") - - var recipients []string - for _, email := range be.Emails { - recipients = append(recipients, email.To[0]) - } - sort.SliceStable(recipients, func(i, j int) bool { - r1 := recipients[i] - r2 := recipients[j] - - return r1 < r2 - }) - - assert.DeepEqual(t, recipients, []string{"alice@example.com", "chuck@example.com"}, "email address mismatch") -} diff --git a/pkg/server/mailer/templates/src/reset_password.txt b/pkg/server/mailer/templates/src/reset_password.txt index a66c0ba8..3bc34850 100644 --- a/pkg/server/mailer/templates/src/reset_password.txt +++ b/pkg/server/mailer/templates/src/reset_password.txt @@ -6,4 +6,4 @@ Please click on the following link, or paste this into your browser to complete You can reply to this message, if you have questions. -- Sung (Maker of Dnote) +- Dnote team diff --git a/pkg/server/mailer/templates/src/reset_password_alert.txt b/pkg/server/mailer/templates/src/reset_password_alert.txt index f1d7389b..3aa9bdd6 100644 --- a/pkg/server/mailer/templates/src/reset_password_alert.txt +++ b/pkg/server/mailer/templates/src/reset_password_alert.txt @@ -6,4 +6,4 @@ If you did not initiate this password change, please notify us by replying, and Thanks. -- Sung (Maker of Dnote) +- Dnote team diff --git a/pkg/server/mailer/templates/src/subscription_confirmation.txt b/pkg/server/mailer/templates/src/subscription_confirmation.txt index 03e98a66..e212211f 100644 --- a/pkg/server/mailer/templates/src/subscription_confirmation.txt +++ b/pkg/server/mailer/templates/src/subscription_confirmation.txt @@ -3,10 +3,10 @@ Hi, thanks for signing up for Dnote Pro. Now you can take your notes with you wherever you go! * Synchronize data among an unlimited number of machines. -* Access notes anywhere via the web interface. +* Manage notes via REST API. Your account is "{{ .AccountEmail }}". Log in at {{ .WebURL }}/login Thank you for using Dnote. Your support makes it possible to develop it for developers around the world. -- Sung (Maker of Dnote) +- Dnote team diff --git a/pkg/server/mailer/templates/src/verify_email.txt b/pkg/server/mailer/templates/src/verify_email.txt index 8e40bf89..a85ab705 100644 --- a/pkg/server/mailer/templates/src/verify_email.txt +++ b/pkg/server/mailer/templates/src/verify_email.txt @@ -1,9 +1,9 @@ Hi. -Welcome to Dnote! To verify your email so that you can automate spaced reptition, visit the following link: +Welcome to Dnote! To verify your email, visit the following link: {{ .WebURL }}/verify-email/{{ .Token }} -Thanks for using my software. +Thanks for using Dnote. -- Sung (Maker of Dnote) +- Dnote team diff --git a/pkg/server/mailer/templates/src/welcome.txt b/pkg/server/mailer/templates/src/welcome.txt index daba304a..7a33207a 100644 --- a/pkg/server/mailer/templates/src/welcome.txt +++ b/pkg/server/mailer/templates/src/welcome.txt @@ -13,4 +13,4 @@ Dnote is open source and you can see the source code at https://github.com/dnote Feel free to reply anytime. Thanks for using Dnote. -- Sung (Maker of Dnote) +- Dnote team diff --git a/pkg/server/main.go b/pkg/server/main.go index f1721009..cfd33f15 100644 --- a/pkg/server/main.go +++ b/pkg/server/main.go @@ -21,73 +21,26 @@ package main import ( "flag" "fmt" + "io/ioutil" "log" "net/http" "github.com/dnote/dnote/pkg/clock" - "github.com/dnote/dnote/pkg/server/api" "github.com/dnote/dnote/pkg/server/app" + "github.com/dnote/dnote/pkg/server/buildinfo" "github.com/dnote/dnote/pkg/server/config" + "github.com/dnote/dnote/pkg/server/controllers" "github.com/dnote/dnote/pkg/server/database" "github.com/dnote/dnote/pkg/server/job" "github.com/dnote/dnote/pkg/server/mailer" - "github.com/dnote/dnote/pkg/server/web" + "github.com/dnote/dnote/pkg/server/views" "github.com/jinzhu/gorm" - "github.com/gobuffalo/packr/v2" "github.com/pkg/errors" ) -var versionTag = "master" -var port = flag.String("port", "3000", "port to connect to") -var rootBox *packr.Box - -func init() { - rootBox = packr.New("root", "../../web/public") -} - -func mustFind(box *packr.Box, path string) []byte { - b, err := rootBox.Find(path) - if err != nil { - panic(errors.Wrapf(err, "getting file content for %s", path)) - } - - return b -} - -func initWebContext(db *gorm.DB) web.Context { - staticBox := packr.New("static", "../../web/public/static") - - return web.Context{ - DB: db, - IndexHTML: mustFind(rootBox, "index.html"), - RobotsTxt: mustFind(rootBox, "robots.txt"), - ServiceWorkerJs: mustFind(rootBox, "service-worker.js"), - StaticFileSystem: staticBox, - } -} - -func initServer(a app.App) (*http.ServeMux, error) { - apiRouter, err := api.NewRouter(&api.API{App: &a}) - if err != nil { - return nil, errors.Wrap(err, "initializing router") - } - - webCtx := initWebContext(a.DB) - webHandlers, err := web.Init(webCtx) - if err != nil { - return nil, errors.Wrap(err, "initializing web handlers") - } - - mux := http.NewServeMux() - mux.Handle("/api/", http.StripPrefix("/api", apiRouter)) - mux.Handle("/static/", webHandlers.GetStatic) - mux.HandleFunc("/service-worker.js", webHandlers.GetServiceWorker) - mux.HandleFunc("/robots.txt", webHandlers.GetRobots) - mux.HandleFunc("/", webHandlers.GetRoot) - - return mux, nil -} +var pageDir = flag.String("pageDir", "views", "the path to a directory containing page templates") +var staticDir = flag.String("staticDir", "./static/", "the path to the static directory ") func initDB(c config.Config) *gorm.DB { db, err := gorm.Open("postgres", c.DB.GetConnectionStr()) @@ -99,15 +52,28 @@ func initDB(c config.Config) *gorm.DB { return db } -func initApp(c config.Config) app.App { - db := initDB(c) +func mustReadFile(path string) []byte { + ret, err := ioutil.ReadFile(path) + if err != nil { + panic(errors.Wrap(err, "reading file")) + } + + return ret +} + +func initApp(cfg config.Config) app.App { + db := initDB(cfg) + + files := map[string][]byte{} + files[views.ServerErrorPageFileKey] = mustReadFile(fmt.Sprintf("%s/500.html", cfg.StaticDir)) return app.App{ DB: db, Clock: clock.New(), EmailTemplates: mailer.NewTemplates(nil), EmailBackend: &mailer.SimpleBackendImplementation{}, - Config: c, + Config: cfg, + Files: files, } } @@ -124,34 +90,43 @@ func runJob(a app.App) error { } func startCmd() { - c := config.Load() + cfg := config.Load() + cfg.SetPageTemplateDir(*pageDir) + cfg.SetStaticDir(*staticDir) + cfg.SetAssetBaseURL("/static") - app := initApp(c) + app := initApp(cfg) defer app.DB.Close() if err := database.Migrate(app.DB); err != nil { panic(errors.Wrap(err, "running migrations")) } - if err := runJob(app); err != nil { panic(errors.Wrap(err, "running job")) } - srv, err := initServer(app) - if err != nil { - panic(errors.Wrap(err, "initializing server")) + ctl := controllers.New(&app, *pageDir) + rc := controllers.RouteConfig{ + WebRoutes: controllers.NewWebRoutes(&app, ctl), + APIRoutes: controllers.NewAPIRoutes(&app, ctl), + Controllers: ctl, } - log.Printf("Dnote version %s is running on port %s", versionTag, *port) - log.Fatalln(http.ListenAndServe(":"+*port, srv)) + r, err := controllers.NewRouter(&app, rc) + if err != nil { + panic(errors.Wrap(err, "initializing router")) + } + + log.Printf("Dnote version %s is running on port %s", buildinfo.Version, cfg.Port) + log.Fatalln(http.ListenAndServe(fmt.Sprintf(":%s", cfg.Port), r)) } func versionCmd() { - fmt.Printf("dnote-server-%s\n", versionTag) + fmt.Printf("dnote-server-%s\n", buildinfo.Version) } func rootCmd() { - fmt.Printf(`Dnote Server - A simple personal knowledge base + fmt.Printf(`Dnote server - a simple personal knowledge base Usage: dnote-server [command] diff --git a/pkg/server/handlers/auth.go b/pkg/server/middleware/auth.go similarity index 76% rename from pkg/server/handlers/auth.go rename to pkg/server/middleware/auth.go index 83946dce..79d4d780 100644 --- a/pkg/server/handlers/auth.go +++ b/pkg/server/middleware/auth.go @@ -16,15 +16,16 @@ * along with Dnote. If not, see . */ -package handlers +package middleware import ( - "context" "net/http" + "net/url" "strings" "time" "github.com/dnote/dnote/pkg/server/app" + "github.com/dnote/dnote/pkg/server/context" "github.com/dnote/dnote/pkg/server/database" "github.com/dnote/dnote/pkg/server/helpers" "github.com/dnote/dnote/pkg/server/log" @@ -82,11 +83,18 @@ type AuthParams struct { // Auth is an authentication middleware func Auth(a *app.App, next http.HandlerFunc, p *AuthParams) http.HandlerFunc { + next = WithAccount(a, next) + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - user, ok, err := AuthWithSession(a.DB, r, p) + user, ok, err := AuthWithSession(a.DB, r) if !ok { if p != nil && p.RedirectGuestsToLogin { - http.Redirect(w, r, "/login", http.StatusFound) + + q := url.Values{} + q.Set("referrer", r.URL.Path) + path := helpers.GetPath("/login", &q) + + http.Redirect(w, r, path, http.StatusFound) return } @@ -105,7 +113,24 @@ func Auth(a *app.App, next http.HandlerFunc, p *AuthParams) http.HandlerFunc { } } - ctx := context.WithValue(r.Context(), helpers.KeyUser, user) + ctx := context.WithUser(r.Context(), &user) + next.ServeHTTP(w, r.WithContext(ctx)) + }) + +} + +func WithAccount(a *app.App, next http.HandlerFunc) http.HandlerFunc { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + user := context.User(r.Context()) + + var account database.Account + if err := a.DB.Where("user_id = ?", user.ID).First(&account).Error; err != nil { + DoError(w, "finding account", err, http.StatusInternalServerError) + return + } + + ctx := context.WithAccount(r.Context(), &account) + next.ServeHTTP(w, r.WithContext(ctx)) }) } @@ -122,10 +147,10 @@ func TokenAuth(a *app.App, next http.HandlerFunc, tokenType string, p *AuthParam ctx := r.Context() if ok { - ctx = context.WithValue(ctx, helpers.KeyToken, token) + ctx = context.WithToken(ctx, &token) } else { // If token-based auth fails, fall back to session-based auth - user, ok, err = AuthWithSession(a.DB, r, p) + user, ok, err = AuthWithSession(a.DB, r) if err != nil { DoError(w, "authenticating with session", err, http.StatusInternalServerError) return @@ -144,13 +169,13 @@ func TokenAuth(a *app.App, next http.HandlerFunc, tokenType string, p *AuthParam } } - ctx = context.WithValue(ctx, helpers.KeyUser, user) + ctx = context.WithUser(ctx, &user) next.ServeHTTP(w, r.WithContext(ctx)) }) } // AuthWithSession performs user authentication with session -func AuthWithSession(db *gorm.DB, r *http.Request, p *AuthParams) (database.User, bool, error) { +func AuthWithSession(db *gorm.DB, r *http.Request) (database.User, bool, error) { var user database.User sessionKey, err := GetCredential(r) @@ -184,3 +209,19 @@ func AuthWithSession(db *gorm.DB, r *http.Request, p *AuthParams) (database.User return user, true, nil } + +func GuestOnly(a *app.App, next http.HandlerFunc) http.HandlerFunc { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + _, ok, err := AuthWithSession(a.DB, r) + if err != nil { + // log the error and continue + log.ErrorWrap(err, "authenticating with session") + } + + if ok { + http.Redirect(w, r, "/", http.StatusFound) + } else { + next.ServeHTTP(w, r) + } + }) +} diff --git a/pkg/server/handlers/helpers.go b/pkg/server/middleware/helpers.go similarity index 91% rename from pkg/server/handlers/helpers.go rename to pkg/server/middleware/helpers.go index 8c0d51a7..f958d573 100644 --- a/pkg/server/handlers/helpers.go +++ b/pkg/server/middleware/helpers.go @@ -16,10 +16,9 @@ * along with Dnote. If not, see . */ -package handlers +package middleware import ( - "encoding/json" "net/http" "strings" "time" @@ -90,16 +89,6 @@ func DoError(w http.ResponseWriter, msg string, err error, statusCode int) { http.Error(w, statusText, statusCode) } -// RespondJSON encodes the given payload into a JSON format and writes it to the given response writer -func RespondJSON(w http.ResponseWriter, statusCode int, payload interface{}) { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(statusCode) - - if err := json.NewEncoder(w).Encode(payload); err != nil { - DoError(w, "encoding response", err, http.StatusInternalServerError) - } -} - // NotSupported is the handler for the route that is no longer supported func NotSupported(w http.ResponseWriter, r *http.Request) { http.Error(w, "API version is not supported. Please upgrade your client.", http.StatusGone) diff --git a/pkg/server/handlers/helpers_test.go b/pkg/server/middleware/helpers_test.go similarity index 98% rename from pkg/server/handlers/helpers_test.go rename to pkg/server/middleware/helpers_test.go index f82e6ea0..1eb9fe7a 100644 --- a/pkg/server/handlers/helpers_test.go +++ b/pkg/server/middleware/helpers_test.go @@ -16,7 +16,7 @@ * along with Dnote. If not, see . */ -package handlers +package middleware import ( "fmt" @@ -184,6 +184,8 @@ func TestAuthMiddleware(t *testing.T) { defer testutils.ClearData(testutils.DB) user := testutils.SetupUserData() + testutils.SetupAccountData(user, "alice@test.com", "pass1234") + session := database.Session{ Key: "A9xgggqzTHETy++GDi1NpDNe0iyqosPm9bitdeNGkJU=", UserID: user.ID, @@ -405,13 +407,15 @@ func TestAuthMiddleware_RedirectGuestsToLogin(t *testing.T) { // test assert.Equal(t, res.StatusCode, http.StatusFound, "status code mismatch") - assert.Equal(t, res.Header.Get("Location"), "/login", "location header mismatch") + assert.Equal(t, res.Header.Get("Location"), "/login?referrer=%2F", "location header mismatch") }) t.Run("logged in user", func(t *testing.T) { req := testutils.MakeReq(server.URL, "GET", "/", "") user := testutils.SetupUserData() + testutils.SetupAccountData(user, "alice@test.com", "pass1234") + testutils.MustExec(t, testutils.DB.Model(&user).Update("cloud", false), "preparing session") session := database.Session{ Key: "A9xgggqzTHETy++GDi1NpDNe0iyqosPm9bitdeNGkJU=", diff --git a/pkg/server/handlers/limit.go b/pkg/server/middleware/limit.go similarity index 92% rename from pkg/server/handlers/limit.go rename to pkg/server/middleware/limit.go index 9859e7e2..bdc5de40 100644 --- a/pkg/server/handlers/limit.go +++ b/pkg/server/middleware/limit.go @@ -16,10 +16,11 @@ * along with Dnote. If not, see . */ -package handlers +package middleware import ( "net/http" + "os" "strings" "sync" "time" @@ -122,3 +123,14 @@ func Limit(next http.Handler) http.HandlerFunc { next.ServeHTTP(w, r) }) } + +// ApplyLimit applies rate limit conditionally +func ApplyLimit(h http.HandlerFunc, rateLimit bool) http.Handler { + ret := h + + if rateLimit && os.Getenv("GO_ENV") != "TEST" { + ret = Limit(ret) + } + + return ret +} diff --git a/pkg/server/handlers/logging.go b/pkg/server/middleware/logging.go similarity index 97% rename from pkg/server/handlers/logging.go rename to pkg/server/middleware/logging.go index 98e36597..85bfbf32 100644 --- a/pkg/server/handlers/logging.go +++ b/pkg/server/middleware/logging.go @@ -16,7 +16,7 @@ * along with Dnote. If not, see . */ -package handlers +package middleware import ( "fmt" @@ -39,7 +39,7 @@ func (w *logResponseWriter) WriteHeader(code int) { w.ResponseWriter.WriteHeader(code) } -// Logging is a logging middleware +// logging is a logging middleware func Logging(inner http.Handler) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { start := time.Now() diff --git a/pkg/server/api/main_test.go b/pkg/server/middleware/main_test.go similarity index 98% rename from pkg/server/api/main_test.go rename to pkg/server/middleware/main_test.go index cca6ae29..08a3acf1 100644 --- a/pkg/server/api/main_test.go +++ b/pkg/server/middleware/main_test.go @@ -16,7 +16,7 @@ * along with Dnote. If not, see . */ -package api +package middleware import ( "os" diff --git a/pkg/server/middleware/middleware.go b/pkg/server/middleware/middleware.go new file mode 100644 index 00000000..44f6377a --- /dev/null +++ b/pkg/server/middleware/middleware.go @@ -0,0 +1,76 @@ +package middleware + +import ( + "net/http" + "net/url" + + "github.com/dnote/dnote/pkg/server/app" + "github.com/gorilla/schema" +) + +// Middleware is a middleware for request handlers +type Middleware func(h http.Handler, app *app.App, rateLimit bool) http.Handler + +type payload struct { + Method string `schema:"_method"` +} + +func parseValues(values url.Values, dst interface{}) error { + dec := schema.NewDecoder() + + // Ignore CSRF token field + dec.IgnoreUnknownKeys(true) + + if err := dec.Decode(dst, values); err != nil { + return err + } + + return nil +} + +// methodOverrideKey is the form key for overriding the method +var methodOverrideKey = "_method" + +// methodOverride overrides the request's method to simulate form actions that +// are not natively supported by web browsers +func methodOverride(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodPost { + method := r.PostFormValue(methodOverrideKey) + + if method == http.MethodPut || method == http.MethodPatch || method == http.MethodDelete { + r.Method = method + } + } + + next.ServeHTTP(w, r) + }) +} + +// WebMw is the middleware for the web +func WebMw(h http.Handler, app *app.App, rateLimit bool) http.Handler { + ret := h + + ret = ApplyLimit(ret.ServeHTTP, rateLimit) + + return ret +} + +// APIMw is the middleware for the API +func APIMw(h http.Handler, app *app.App, rateLimit bool) http.Handler { + ret := h + + ret = ApplyLimit(ret.ServeHTTP, rateLimit) + + return ret +} + +// Global is the middleware for all routes +func Global(h http.Handler) http.Handler { + ret := h + + ret = Logging(ret) + ret = methodOverride(ret) + + return ret +} diff --git a/pkg/server/net/writer.go b/pkg/server/net/writer.go new file mode 100644 index 00000000..912d44d4 --- /dev/null +++ b/pkg/server/net/writer.go @@ -0,0 +1,31 @@ +package net + +import ( + "github.com/dnote/dnote/pkg/server/log" + "net/http" +) + +// LifecycleWriter wraps http.ResponseWriter to track state of the http response. +// The optional interfaces of http.ResponseWriter are lost because of the wrapping, and +// such interfaces should be implemented if needed. (i.e. http.Pusher, http.Flusher, etc.) +type LifecycleWriter struct { + http.ResponseWriter + StatusCode int +} + +// WriteHeader wraps the WriteHeader call and marks the response state as done. +func (w *LifecycleWriter) WriteHeader(code int) { + w.StatusCode = code + w.ResponseWriter.WriteHeader(code) +} + +// IsHeaderWritten returns true if a response has been written. +func IsHeaderWritten(w http.ResponseWriter) bool { + if lw, ok := w.(*LifecycleWriter); ok { + return lw.StatusCode != 0 + } + + // the response writer must have been wrapped in the middleware chain. + log.Error("unable to log because writer is not a LifecycleWriter") + return false +} diff --git a/pkg/server/operations/notes.go b/pkg/server/operations/notes.go index 22bb8468..1106ab5e 100644 --- a/pkg/server/operations/notes.go +++ b/pkg/server/operations/notes.go @@ -27,7 +27,7 @@ import ( ) // GetNote retrieves a note for the given user -func GetNote(db *gorm.DB, uuid string, user database.User) (database.Note, bool, error) { +func GetNote(db *gorm.DB, uuid string, user *database.User) (database.Note, bool, error) { zeroNote := database.Note{} if !helpers.ValidateUUID(uuid) { return zeroNote, false, nil @@ -45,7 +45,7 @@ func GetNote(db *gorm.DB, uuid string, user database.User) (database.Note, bool, return zeroNote, false, errors.Wrap(err, "finding note") } - if ok := permissions.ViewNote(&user, note); !ok { + if ok := permissions.ViewNote(user, note); !ok { return zeroNote, false, nil } diff --git a/pkg/server/operations/notes_test.go b/pkg/server/operations/notes_test.go index 0ea93878..ceb0728f 100644 --- a/pkg/server/operations/notes_test.go +++ b/pkg/server/operations/notes_test.go @@ -107,7 +107,7 @@ func TestGetNote(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - note, ok, err := GetNote(testutils.DB, tc.note.UUID, tc.user) + note, ok, err := GetNote(testutils.DB, tc.note.UUID, &tc.user) if err != nil { t.Fatal(errors.Wrap(err, "executing")) } @@ -141,7 +141,7 @@ func TestGetNote_nonexistent(t *testing.T) { testutils.MustExec(t, testutils.DB.Save(&n1), "preparing n1") nonexistentUUID := "4fd19336-671e-4ff3-8f22-662b80e22edd" - note, ok, err := GetNote(testutils.DB, nonexistentUUID, user) + note, ok, err := GetNote(testutils.DB, nonexistentUUID, &user) if err != nil { t.Fatal(errors.Wrap(err, "executing")) } diff --git a/pkg/server/static/main.css b/pkg/server/static/main.css new file mode 100644 index 00000000..131ab48d --- /dev/null +++ b/pkg/server/static/main.css @@ -0,0 +1,12 @@ +/*! + * Bootstrap Reboot v4.3.1 (https://getbootstrap.com/) + * Copyright 2011-2019 The Bootstrap Authors + * Copyright 2011-2019 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) + */*,*::before,*::after{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0)}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0 !important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[title],abbr[data-original-title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul,dl{margin-top:0;margin-bottom:1rem}ol ol,ul ul,ol ul,ul ol{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}a{color:#007bff;text-decoration:none;background-color:rgba(0,0,0,0)}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):hover,a:not([href]):not([tabindex]):focus{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}pre,code,kbd,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}input,button,select,optgroup,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}select{word-wrap:normal}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button}button:not(:disabled),[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled){cursor:pointer}button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{padding:0;border-style:none}input[type=radio],input[type=checkbox]{box-sizing:border-box;padding:0}input[type=date],input[type=time],input[type=datetime-local],input[type=month]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none !important}/*! + * Bootstrap Grid v4.3.1 (https://getbootstrap.com/) + * Copyright 2011-2019 The Bootstrap Authors + * Copyright 2011-2019 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */html{box-sizing:border-box;-ms-overflow-style:scrollbar}*,*::before,*::after{box-sizing:inherit}.container-wide{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media(min-width: 576px){.container-wide{max-width:540px}}@media(min-width: 768px){.container-wide{max-width:720px}}@media(min-width: 992px){.container-wide{max-width:960px}}@media(min-width: 1200px){.container-wide{max-width:1040px}}@media(min-width: 1440px){.container-wide{max-width:1280px}}@media(min-width: 1800px){.container-wide{max-width:1660px}}.container-fluid{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media(min-width: 576px){.container{max-width:540px}}@media(min-width: 768px){.container{max-width:720px}}@media(min-width: 992px){.container{max-width:960px}}@media(min-width: 1200px){.container{max-width:1280px}}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col-1,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-10,.col-11,.col-12,.col,.col-auto,.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm,.col-sm-auto,.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12,.col-md,.col-md-auto,.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg,.col-lg-auto,.col-xl-1,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl,.col-xl-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-ms-flex-order:-1;order:-1}.order-last{-ms-flex-order:13;order:13}.order-0{-ms-flex-order:0;order:0}.order-1{-ms-flex-order:1;order:1}.order-2{-ms-flex-order:2;order:2}.order-3{-ms-flex-order:3;order:3}.order-4{-ms-flex-order:4;order:4}.order-5{-ms-flex-order:5;order:5}.order-6{-ms-flex-order:6;order:6}.order-7{-ms-flex-order:7;order:7}.order-8{-ms-flex-order:8;order:8}.order-9{-ms-flex-order:9;order:9}.order-10{-ms-flex-order:10;order:10}.order-11{-ms-flex-order:11;order:11}.order-12{-ms-flex-order:12;order:12}.offset-1{margin-left:8.333333%}.offset-2{margin-left:16.666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.333333%}.offset-5{margin-left:41.666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.333333%}.offset-8{margin-left:66.666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.333333%}.offset-11{margin-left:91.666667%}@media(min-width: 576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-sm-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-sm-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-sm-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-sm-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-sm-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-sm-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-sm-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-sm-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-ms-flex-order:-1;order:-1}.order-sm-last{-ms-flex-order:13;order:13}.order-sm-0{-ms-flex-order:0;order:0}.order-sm-1{-ms-flex-order:1;order:1}.order-sm-2{-ms-flex-order:2;order:2}.order-sm-3{-ms-flex-order:3;order:3}.order-sm-4{-ms-flex-order:4;order:4}.order-sm-5{-ms-flex-order:5;order:5}.order-sm-6{-ms-flex-order:6;order:6}.order-sm-7{-ms-flex-order:7;order:7}.order-sm-8{-ms-flex-order:8;order:8}.order-sm-9{-ms-flex-order:9;order:9}.order-sm-10{-ms-flex-order:10;order:10}.order-sm-11{-ms-flex-order:11;order:11}.order-sm-12{-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.333333%}.offset-sm-2{margin-left:16.666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.333333%}.offset-sm-5{margin-left:41.666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.333333%}.offset-sm-8{margin-left:66.666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.333333%}.offset-sm-11{margin-left:91.666667%}}@media(min-width: 768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-md-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-md-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-md-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-md-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-md-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-md-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-md-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-md-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-ms-flex-order:-1;order:-1}.order-md-last{-ms-flex-order:13;order:13}.order-md-0{-ms-flex-order:0;order:0}.order-md-1{-ms-flex-order:1;order:1}.order-md-2{-ms-flex-order:2;order:2}.order-md-3{-ms-flex-order:3;order:3}.order-md-4{-ms-flex-order:4;order:4}.order-md-5{-ms-flex-order:5;order:5}.order-md-6{-ms-flex-order:6;order:6}.order-md-7{-ms-flex-order:7;order:7}.order-md-8{-ms-flex-order:8;order:8}.order-md-9{-ms-flex-order:9;order:9}.order-md-10{-ms-flex-order:10;order:10}.order-md-11{-ms-flex-order:11;order:11}.order-md-12{-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.333333%}.offset-md-2{margin-left:16.666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.333333%}.offset-md-5{margin-left:41.666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.333333%}.offset-md-8{margin-left:66.666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.333333%}.offset-md-11{margin-left:91.666667%}}@media(min-width: 992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-lg-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-lg-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-lg-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-lg-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-lg-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-lg-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-lg-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-lg-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-ms-flex-order:-1;order:-1}.order-lg-last{-ms-flex-order:13;order:13}.order-lg-0{-ms-flex-order:0;order:0}.order-lg-1{-ms-flex-order:1;order:1}.order-lg-2{-ms-flex-order:2;order:2}.order-lg-3{-ms-flex-order:3;order:3}.order-lg-4{-ms-flex-order:4;order:4}.order-lg-5{-ms-flex-order:5;order:5}.order-lg-6{-ms-flex-order:6;order:6}.order-lg-7{-ms-flex-order:7;order:7}.order-lg-8{-ms-flex-order:8;order:8}.order-lg-9{-ms-flex-order:9;order:9}.order-lg-10{-ms-flex-order:10;order:10}.order-lg-11{-ms-flex-order:11;order:11}.order-lg-12{-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.333333%}.offset-lg-2{margin-left:16.666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.333333%}.offset-lg-5{margin-left:41.666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.333333%}.offset-lg-8{margin-left:66.666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.333333%}.offset-lg-11{margin-left:91.666667%}}@media(min-width: 1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-xl-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-xl-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-xl-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-xl-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-xl-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-xl-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-xl-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-xl-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-ms-flex-order:-1;order:-1}.order-xl-last{-ms-flex-order:13;order:13}.order-xl-0{-ms-flex-order:0;order:0}.order-xl-1{-ms-flex-order:1;order:1}.order-xl-2{-ms-flex-order:2;order:2}.order-xl-3{-ms-flex-order:3;order:3}.order-xl-4{-ms-flex-order:4;order:4}.order-xl-5{-ms-flex-order:5;order:5}.order-xl-6{-ms-flex-order:6;order:6}.order-xl-7{-ms-flex-order:7;order:7}.order-xl-8{-ms-flex-order:8;order:8}.order-xl-9{-ms-flex-order:9;order:9}.order-xl-10{-ms-flex-order:10;order:10}.order-xl-11{-ms-flex-order:11;order:11}.order-xl-12{-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.333333%}.offset-xl-2{margin-left:16.666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.333333%}.offset-xl-5{margin-left:41.666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.333333%}.offset-xl-8{margin-left:66.666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.333333%}.offset-xl-11{margin-left:91.666667%}}.form-control{display:block;width:100%;padding:.375rem .75rem;font-size:1rem;line-height:1.5;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}.alert{position:relative;padding:1.75rem 1.25rem;border:1px solid rgba(0,0,0,0)}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible .close{position:absolute;top:0;right:0;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#004085;background-color:#cce5ff;border-color:#b8daff}.alert-primary hr{border-top-color:#9fcdff}.alert-primary .alert-link{color:#002752}.alert-secondary{color:#383d41;background-color:#e2e3e5;border-color:#d6d8db}.alert-secondary hr{border-top-color:#c8cbcf}.alert-secondary .alert-link{color:#202326}.alert-success{color:#155724;background-color:#d4edda;border-color:#c3e6cb}.alert-success hr{border-top-color:#b1dfbb}.alert-success .alert-link{color:#0b2e13}.alert-info{color:#0c5460;background-color:#d1ecf1;border-color:#bee5eb}.alert-info hr{border-top-color:#abdde5}.alert-info .alert-link{color:#062c33}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.alert-warning hr{border-top-color:#ffe8a1}.alert-warning .alert-link{color:#533f03}.alert-danger{color:#721c24;background-color:#f8d7da;border-color:#f5c6cb}.alert-danger hr{border-top-color:#f1b0b7}.alert-danger .alert-link{color:#491217}.alert-light{color:#818182;background-color:#fefefe;border-color:#fdfdfe}.alert-light hr{border-top-color:#ececf6}.alert-light .alert-link{color:#686868}.alert-dark{color:#1b1e21;background-color:#d6d8d9;border-color:#c6c8ca}.alert-dark hr{border-top-color:#b9bbbe}.alert-dark .alert-link{color:#040505}.alert-slim{padding:.75rem 1.25rem}:export{mdBreakpoint:576px;smBreakpoint:321px}.button{position:relative;display:inline-block;text-align:center;white-space:nowrap;vertical-align:middle;user-select:none;border-image:initial;transition-property:color,box-shadow;transition-duration:.2s;transition-timing-function:ease-in-out;text-decoration:none;cursor:pointer}.button:not(.button-no-ui){border-width:1px;border-style:solid;border-color:rgba(0,0,0,0)}.button:not(:disabled):hover{text-decoration:none}.button:disabled{cursor:not-allowed;opacity:.6}.button:focus{outline:2px dotted #9c9c9c}button:disabled{cursor:not-allowed;opacity:.6}.button-small{font-size:14px;font-size:1.4rem;padding:.4rem 1.2rem}@media(min-width: 576px){.button-small{font-size:14px;font-size:1.4rem}}@media(min-width: 992px){.button-small{font-size:14px;font-size:1.4rem}}.button-normal{padding:.8rem 1.6rem}.button-large{font-size:18px;font-size:1.8rem;padding:.8rem 2.4rem}@media(min-width: 576px){.button-large{font-size:18px;font-size:1.8rem}}@media(min-width: 992px){.button-large{font-size:18px;font-size:1.8rem}}@media(min-width: 576px){.button-large{padding:1.2rem 3.6rem}}@media(min-width: 992px){.button-large{padding:1.2rem 4.8rem}}.button-xlarge{font-size:19.2px;font-size:1.92rem;padding:1.6rem 2.4rem}@media(min-width: 576px){.button-xlarge{font-size:21.6px;font-size:2.16rem}}@media(min-width: 992px){.button-xlarge{font-size:24px;font-size:2.4rem}}@media(min-width: 576px){.button-xlarge{padding:1.2rem 3.6rem}}@media(min-width: 992px){.button-xlarge{padding:1.6rem 4.8rem}}.button-first{color:#fff;background-color:#333745}.button-first:not(:disabled):hover{color:#fff;background-color:#282b36;box-shadow:0px 0px 4px 2px #cacaca}.button-first-outline{background:rgba(0,0,0,0);color:#333745}.button-first-outline:not(.button-no-ui){border-color:#333745;border-width:2px}.button-first-outline:not(:disabled):hover{color:#333745;box-shadow:0px 0px 4px 2px #cacaca}.button-second{color:#2a2a2a;background-color:#e7e7e7}.button-second:not(:disabled):hover{color:#2a2a2a;background-color:#dadada;box-shadow:0px 0px 4px 2px #cacaca}.button-second-outline{background:rgba(0,0,0,0);color:#2a2a2a}.button-second-outline:not(.button-no-ui){border-color:#e7e7e7;border-width:2px}.button-second-outline:not(:disabled):hover{color:#2a2a2a;box-shadow:0px 0px 4px 2px #cacaca}.button-third{color:#fff;background-color:#0a4b73}.button-third:not(:disabled):hover{color:#fff;background-color:#083c5c;box-shadow:0px 0px 4px 2px #cacaca}.button-third-outline{background:rgba(0,0,0,0);color:#0a4b73}.button-third-outline:not(.button-no-ui){border-color:#0a4b73;border-width:2px}.button-third-outline:not(:disabled):hover{color:#0a4b73;box-shadow:0px 0px 4px 2px #cacaca}.button-danger{background:rgba(0,0,0,0);color:#cb2431;font-weight:600}.button-danger:not(.button-no-ui){border-color:#cb2431;border-width:2px}.button-danger:not(:disabled):hover{color:#cb2431;box-shadow:0px 0px 4px 2px #cacaca}.button-stretch{width:100%}.button~.button{margin-left:1.2rem}.button-no-ui{border:none;background:none;text-align:left;cursor:pointer}.button-no-padding{padding:0}.button-link{color:#6f53c0}.button-link:hover{color:#6143b7;text-decoration:underline}:export{mdBreakpoint:576px;smBreakpoint:321px}.Select{position:relative}.Select input::-webkit-contacts-auto-fill-button,.Select input::-webkit-credentials-auto-fill-button{display:none !important}.Select input::-ms-clear{display:none !important}.Select input::-ms-reveal{display:none !important}.Select,.Select div,.Select input,.Select span{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.Select.is-disabled .Select-arrow-zone{cursor:default;pointer-events:none;opacity:.35}.Select.is-disabled>.Select-control{background-color:#f9f9f9}.Select.is-disabled>.Select-control:hover{box-shadow:none}.Select.is-open>.Select-control{border-bottom-right-radius:0;border-bottom-left-radius:0;background:#fff}.Select.is-open>.Select-control .Select-arrow{top:-2px;border-color:rgba(0,0,0,0) rgba(0,0,0,0) #999;border-width:0 5px 5px}.Select.is-searchable.is-open>.Select-control{cursor:text}.Select.is-searchable.is-focused:not(.is-open)>.Select-control{cursor:text}.Select.is-focused>.Select-control{background:#fff}.Select.is-focused>.Select-control{border-top-color:#b6c9e9;border-bottom-color:#b6c9e9}.Select.is-focused:not(.is-open)>.Select-control{background:#fff}.Select.has-value.is-clearable.Select--single>.Select-control .Select-value{padding-right:42px}.Select.has-value.Select--single>.Select-control .Select-value .Select-value-label,.Select.has-value.is-pseudo-focused.Select--single>.Select-control .Select-value .Select-value-label{color:#333}.Select.has-value.Select--single>.Select-control .Select-value a.Select-value-label,.Select.has-value.is-pseudo-focused.Select--single>.Select-control .Select-value a.Select-value-label{cursor:pointer;text-decoration:none}.Select.has-value.Select--single>.Select-control .Select-value a.Select-value-label:hover,.Select.has-value.is-pseudo-focused.Select--single>.Select-control .Select-value a.Select-value-label:hover,.Select.has-value.Select--single>.Select-control .Select-value a.Select-value-label:focus,.Select.has-value.is-pseudo-focused.Select--single>.Select-control .Select-value a.Select-value-label:focus{color:#007eff;outline:none;text-decoration:underline}.Select.has-value.Select--single>.Select-control .Select-value a.Select-value-label:focus,.Select.has-value.is-pseudo-focused.Select--single>.Select-control .Select-value a.Select-value-label:focus{background:#fff}.Select.has-value.is-pseudo-focused .Select-input{opacity:0}.Select.is-open .Select-arrow,.Select .Select-arrow-zone:hover>.Select-arrow{border-top-color:#666}.Select.Select--rtl{direction:rtl;text-align:right}.Select-control{background-color:#fff;color:#333;cursor:default;display:table;border-top:1px solid #e2e2e2;border-bottom:1px solid #e2e2e2;height:36px;outline:none;overflow:hidden;position:relative;width:100%}.Select-control .Select-input:focus{outline:none;background:#fff}.Select-placeholder,.Select--single>.Select-control .Select-value{bottom:0;font-weight:300;color:#c3c3c3;left:0;line-height:34px;padding-left:16px;padding-right:10px;position:absolute;right:0;top:0;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.Select-input{height:34px;padding-left:10px;padding-right:10px;vertical-align:middle}.Select-input>input{width:100%;background:none rgba(0,0,0,0);border:0 none;box-shadow:none;cursor:default;display:inline-block;font-family:inherit;font-size:inherit;margin:0;outline:none;line-height:17px;padding:8px 0 12px;-webkit-appearance:none}.is-focused .Select-input>input{cursor:text}.has-value.is-pseudo-focused .Select-input{opacity:0}.Select-control:not(.is-searchable)>.Select-input{outline:none}.Select-loading-zone{cursor:pointer;display:table-cell;position:relative;text-align:center;vertical-align:middle;width:16px}.Select-loading{-webkit-animation:Select-animation-spin 400ms infinite linear;-o-animation:Select-animation-spin 400ms infinite linear;animation:Select-animation-spin 400ms infinite linear;width:16px;height:16px;box-sizing:border-box;border-radius:50%;border:2px solid #ccc;border-right-color:#333;display:inline-block;position:relative;vertical-align:middle}.Select-clear-zone{-webkit-animation:Select-animation-fadeIn 200ms;-o-animation:Select-animation-fadeIn 200ms;animation:Select-animation-fadeIn 200ms;color:#999;cursor:pointer;display:table-cell;position:relative;text-align:center;vertical-align:middle;width:17px}.Select-clear-zone:hover{color:#d0021b}.Select-clear{display:inline-block;font-size:18px;line-height:1}.Select--multi .Select-clear-zone{width:17px}.Select-arrow-zone{cursor:pointer;display:table-cell;position:relative;text-align:center;vertical-align:middle;width:25px;padding-right:5px}.Select--rtl .Select-arrow-zone{padding-right:0;padding-left:5px}.Select-arrow{border-color:#999 rgba(0,0,0,0) rgba(0,0,0,0);border-style:solid;border-width:5px 5px 2.5px;display:inline-block;height:0;width:0;position:relative}.Select-control>*:last-child{padding-right:5px}.Select--multi .Select-multi-value-wrapper{display:inline-block}.Select .Select-aria-only{position:absolute;display:inline-block;height:1px;width:1px;margin:-1px;clip:rect(0, 0, 0, 0);overflow:hidden;float:left}@-webkit-keyframes Select-animation-fadeIn{from{opacity:0}to{opacity:1}}@keyframes Select-animation-fadeIn{from{opacity:0}to{opacity:1}}.Select-menu-outer{border-bottom-right-radius:4px;border-bottom-left-radius:4px;background-color:#fff;border:1px solid #ccc;border-top-color:#e6e6e6;box-shadow:0 1px 0 rgba(0,0,0,.06);box-sizing:border-box;margin-top:-1px;max-height:200px;position:absolute;left:0;top:100%;width:100%;z-index:1;-webkit-overflow-scrolling:touch}.Select.is-open>.Select-menu-outer{border-top-color:#b6c9e9}.Select-menu{max-height:150px;overflow-y:auto}.Select-option{box-sizing:border-box;background-color:#fff;color:#666;cursor:pointer;display:block;padding:8px 10px}.Select-option:last-child{border-bottom-right-radius:4px;border-bottom-left-radius:4px}.Select-option.is-selected{background-color:#f5faff;background-color:rgba(0,126,255,.04);color:#333}.Select-option.is-focused{background-color:#ebf5ff;background-color:rgba(0,126,255,.08);color:#333}.Select-option.is-disabled{color:#ccc;cursor:default}.Select-noresults{box-sizing:border-box;color:#999;cursor:default;display:block;padding:8px 10px}.Select--multi .Select-input{vertical-align:middle;margin-left:10px;padding:0}.Select--multi.Select--rtl .Select-input{margin-left:0;margin-right:10px}.Select--multi.has-value .Select-input{margin-left:5px}.Select--multi .Select-value{background-color:#ebf5ff;background-color:rgba(0,126,255,.08);border-radius:2px;border:1px solid #c2e0ff;border:1px solid rgba(0,126,255,.24);color:#007eff;display:inline-block;font-size:.9em;line-height:1.4;margin-left:5px;margin-top:5px;vertical-align:top}.Select--multi .Select-value-icon,.Select--multi .Select-value-label{display:inline-block;vertical-align:middle}.Select--multi .Select-value-label{border-bottom-right-radius:2px;border-top-right-radius:2px;cursor:default;padding:2px 5px}.Select--multi a.Select-value-label{color:#007eff;cursor:pointer;text-decoration:none}.Select--multi a.Select-value-label:hover{text-decoration:underline}.Select--multi .Select-value-icon{cursor:pointer;border-bottom-left-radius:2px;border-top-left-radius:2px;border-right:1px solid #c2e0ff;border-right:1px solid rgba(0,126,255,.24);padding:1px 5px 3px}.Select--multi .Select-value-icon:hover,.Select--multi .Select-value-icon:focus{background-color:#d8eafd;background-color:rgba(0,113,230,.08);color:#0071e6}.Select--multi .Select-value-icon:active{background-color:#c2e0ff;background-color:rgba(0,126,255,.24)}.Select--multi.Select--rtl .Select-value{margin-left:0;margin-right:5px}.Select--multi.Select--rtl .Select-value-icon{border-right:none;border-left:1px solid #c2e0ff;border-left:1px solid rgba(0,126,255,.24)}.Select--multi.is-disabled .Select-value{background-color:#fcfcfc;border:1px solid #e3e3e3;color:#333}.Select--multi.is-disabled .Select-value-icon{cursor:not-allowed;border-right:1px solid #e3e3e3}.Select--multi.is-disabled .Select-value-icon:hover,.Select--multi.is-disabled .Select-value-icon:focus,.Select--multi.is-disabled .Select-value-icon:active{background-color:#fcfcfc}@keyframes Select-animation-spin{to{transform:rotate(1turn)}}@-webkit-keyframes Select-animation-spin{to{-webkit-transform:rotate(1turn)}}:export{mdBreakpoint:576px;smBreakpoint:321px}:export{mdBreakpoint:576px;smBreakpoint:321px}@keyframes holderPulse{0%{opacity:.4}50%{opacity:1}100%{opacity:.4}}.holder{animation:holderPulse 800ms infinite;background:#f4f4f4}.holder.holder-dark{background:#e6e6e6}input[type=text]:disabled,input[type=email]:disabled,input[type=number]:disabled,input[type=password]:disabled,textarea:disabled{background-color:#f3f3f3;cursor:not-allowed}.list-unstyled{list-style:none;padding-left:0;margin-bottom:0}.sr-only{display:none}.scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}button img,button svg{display:block}.text-input{border:1px solid #d8d8d8;padding:.8rem 1.2rem;position:relative;border-radius:.4rem;display:block}.text-input::placeholder{color:#686868}.text-input:focus{border-color:#ecf4ff;box-shadow:inset 0 1px 2px rgba(24,31,35,.075),0 0 0 .2em rgba(4,100,210,.3);outline:none}.text-input-small{padding:.4rem 1.2rem}.text-input-medium{padding:.8rem 1.2rem}.text-input-stretch{width:100%}.label-full{width:100%}a{color:#6f53c0}a:hover{color:#6143b7}h1,h2,h3,h4,h5,h6{margin-bottom:0}@media(max-width: 991px){.container.mobile-fw{max-width:100%}}@media(max-width: 991px){.container.mobile-nopadding{padding-left:0;padding-right:0}.container.mobile-nopadding .row{margin-left:0;margin-right:0}.container.mobile-nopadding [class*=col-]{padding-left:0;padding-right:0}}html body{overflow-y:scroll}.page{padding-top:2rem;padding-bottom:2rem}.page.page-mobile-full{padding-top:0;padding-bottom:0}@media(min-width: 992px){.page.page-mobile-full{padding-top:3.2rem;padding-bottom:3.2rem}}.page-header{margin-top:2rem}.page-header.page-header-full{margin-bottom:2rem}@media(min-width: 992px){.page-header{margin-bottom:2rem;margin-top:0}}.form-select{appearance:none;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAUCAYAAACEYr13AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAACeSURBVHgBzZPBCYQwFERn2Qa2BEuwhJSyHawdrB1oB1qCV6uwhHj0qBXoBPwQRONXEXzwLoEXcCTAzfxogpPEdJyNcZCIWu8CO5+p8WOxoR9NnK3EYrEX/wOxuDmqUcSikejlXfCFfqie5ngE/ie4cVS/ibS0XB4anBhxSaKIU+xQBmLV8m6HZiW2OECEi4/JoXrO78AFHR1oTSvcxQTq7lVcue6CCAAAAABJRU5ErkJggg==");background-color:#fff;background-repeat:no-repeat;background-position:right 8px center;background-size:8px 10px;border:1px solid #d8d8d8;min-height:34px;padding:6px 8px;padding-right:24px;outline:none;vertical-align:middle;border-radius:4px;box-shadow:inset 0 1px 2px rgba(32,36,41,.08)}.form-select:focus{border-color:#2188ff;outline:none;box-shadow:inset 0 1px 2px rgba(32,36,41,.08),0 0 0 2px rgba(3,102,214,.3)}.form-select:disabled,.form-select.form-select-disabled{background-image:url("data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAABAAAAAUCAYAAACEYr13AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAEKSURBVHgBzVTNDYIwFC4NB46OwAi4gY7gETgoE6gTGCcwTgAJ4efGCLCBjMAIXrmA3yOhQazQhJj4JQ0v7fte3/e1hbFfIk3TYxzHp6kc7dtCFEUW5/xBcdM0a9d1S1kel00mSWKCnIkkxDSnXADIMYYEU9O0zPf91WwB6L6NyB3atrUMw7hNFkCbFyROmXYYmypMDMNwo+t6ztSwtW27oEAXrXBuwu2rCht+WPgU7C8gPCBzYOBKhQS5FTwIKBYeQFeJoWyiKNYH5Co6OCuQr/0JdBuPVyElQCd7GRMb3B3HebsHHzexrmvyQvZwqjFZWsDzvCc62BFhSGYD3UMsfs6ToKOd+6EsxgtrtWLW4gUN3AAAAABJRU5ErkJggg==");background-color:#f3f3f3}.input-label{width:auto;font-weight:600;margin-bottom:.4rem;font-size:14px;font-size:1.4rem}@media(min-width: 576px){.input-label{font-size:14px;font-size:1.4rem}}@media(min-width: 992px){.input-label{font-size:14px;font-size:1.4rem}}.page-heading{font-size:19.2px;font-size:1.92rem}@media(min-width: 576px){.page-heading{font-size:21.6px;font-size:2.16rem}}@media(min-width: 992px){.page-heading{font-size:24px;font-size:2.4rem}}.dropdown-caret{display:inline-block;vertical-align:middle;border-top-width:4px;border-top-style:solid;border-right:4px solid rgba(0,0,0,0);border-bottom:0 solid rgba(0,0,0,0);border-left:4px solid rgba(0,0,0,0);margin-left:.8rem}.divider{height:0;overflow:hidden;border-top:1px solid #e9ecef}.marker{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem}.marker-first{color:#fff;background-color:#007bff}.marker-info{color:#fff;background-color:#17a2b8}.markdown-body .anchor{float:left;line-height:1;margin-left:-20px;padding-right:4px}.markdown-body .anchor:focus{outline:none}.markdown-body h1:hover .anchor,.markdown-body h2:hover .anchor,.markdown-body h3:hover .anchor,.markdown-body h4:hover .anchor,.markdown-body h5:hover .anchor,.markdown-body h6:hover .anchor{text-decoration:none}.markdown-body{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;color:#24292e;line-height:1.5;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol;font-size:16px;line-height:1.5;word-wrap:break-word}.markdown-body .pl-c{color:#6a737d}.markdown-body .pl-c1,.markdown-body .pl-s .pl-v{color:#005cc5}.markdown-body .pl-e,.markdown-body .pl-en{color:#6f42c1}.markdown-body .pl-s .pl-s1,.markdown-body .pl-smi{color:#24292e}.markdown-body .pl-ent{color:#22863a}.markdown-body .pl-k{color:#d73a49}.markdown-body .pl-pds,.markdown-body .pl-s,.markdown-body .pl-s .pl-pse .pl-s1,.markdown-body .pl-sr,.markdown-body .pl-sr .pl-cce,.markdown-body .pl-sr .pl-sra,.markdown-body .pl-sr .pl-sre{color:#032f62}.markdown-body .pl-smw,.markdown-body .pl-v{color:#e36209}.markdown-body .pl-bu{color:#b31d28}.markdown-body .pl-ii{background-color:#b31d28;color:#fafbfc}.markdown-body .pl-c2{background-color:#d73a49;color:#fafbfc}.markdown-body .pl-c2:before{content:"^M"}.markdown-body .pl-sr .pl-cce{color:#22863a;font-weight:700}.markdown-body .pl-ml{color:#735c0f}.markdown-body .pl-mh,.markdown-body .pl-mh .pl-en,.markdown-body .pl-ms{color:#005cc5;font-weight:700}.markdown-body .pl-mi{color:#24292e;font-style:italic}.markdown-body .pl-mb{color:#24292e;font-weight:700}.markdown-body .pl-md{background-color:#ffeef0;color:#b31d28}.markdown-body .pl-mi1{background-color:#f0fff4;color:#22863a}.markdown-body .pl-mc{background-color:#ffebda;color:#e36209}.markdown-body .pl-mi2{background-color:#005cc5;color:#f6f8fa}.markdown-body .pl-mdr{color:#6f42c1;font-weight:700}.markdown-body .pl-ba{color:#586069}.markdown-body .pl-sg{color:#959da5}.markdown-body .pl-corl{color:#032f62;text-decoration:underline}.markdown-body details{display:block}.markdown-body summary{display:list-item}.markdown-body a{background-color:rgba(0,0,0,0)}.markdown-body a:active,.markdown-body a:hover{outline-width:0}.markdown-body strong{font-weight:inherit;font-weight:bolder}.markdown-body h1{font-size:2em;margin:.67em 0}.markdown-body img{border-style:none}.markdown-body code,.markdown-body kbd,.markdown-body pre{font-family:monospace,monospace;font-size:1em}.markdown-body hr{box-sizing:content-box;height:0;overflow:visible}.markdown-body input{font:inherit;margin:0}.markdown-body input{overflow:visible}.markdown-body [type=checkbox]{box-sizing:border-box;padding:0}.markdown-body *{box-sizing:border-box}.markdown-body input{font-family:inherit;font-size:inherit;line-height:inherit}.markdown-body a{color:#0366d6;text-decoration:none}.markdown-body a:hover{text-decoration:underline}.markdown-body strong{font-weight:600}.markdown-body hr{background:rgba(0,0,0,0);border:0;border-bottom:1px solid #dfe2e5;height:0;margin:15px 0;overflow:hidden}.markdown-body hr:before{content:"";display:table}.markdown-body hr:after{clear:both;content:"";display:table}.markdown-body table{border-collapse:collapse;border-spacing:0}.markdown-body td,.markdown-body th{padding:0}.markdown-body details summary{cursor:pointer}.markdown-body h1,.markdown-body h2,.markdown-body h3,.markdown-body h4,.markdown-body h5,.markdown-body h6{margin-bottom:0;margin-top:0}.markdown-body h1{font-size:32px}.markdown-body h1,.markdown-body h2{font-weight:600}.markdown-body h2{font-size:24px}.markdown-body h3{font-size:20px}.markdown-body h3,.markdown-body h4{font-weight:600}.markdown-body h4{font-size:16px}.markdown-body h5{font-size:14px}.markdown-body h5,.markdown-body h6{font-weight:600}.markdown-body h6{font-size:12px}.markdown-body p{margin-bottom:10px;margin-top:0}.markdown-body blockquote{margin:0}.markdown-body ol,.markdown-body ul{margin-bottom:0;margin-top:0;padding-left:0}.markdown-body ol ol,.markdown-body ul ol{list-style-type:lower-roman}.markdown-body ol ol ol,.markdown-body ol ul ol,.markdown-body ul ol ol,.markdown-body ul ul ol{list-style-type:lower-alpha}.markdown-body dd{margin-left:0}.markdown-body code,.markdown-body pre{font-family:SFMono-Regular,Consolas,Liberation Mono,Menlo,Courier,monospace;font-size:12px}.markdown-body pre{margin-bottom:0;margin-top:0}.markdown-body input::-webkit-inner-spin-button,.markdown-body input::-webkit-outer-spin-button{-webkit-appearance:none;appearance:none;margin:0}.markdown-body .border{border:1px solid #e1e4e8 !important}.markdown-body .border-0{border:0 !important}.markdown-body .border-bottom{border-bottom:1px solid #e1e4e8 !important}.markdown-body .rounded-1{border-radius:3px !important}.markdown-body .bg-white{background-color:#fff !important}.markdown-body .bg-gray-light{background-color:#fafbfc !important}.markdown-body .text-gray-light{color:#6a737d !important}.markdown-body .mb-0{margin-bottom:0 !important}.markdown-body .my-2{margin-bottom:8px !important;margin-top:8px !important}.markdown-body .pl-0{padding-left:0 !important}.markdown-body .py-0{padding-bottom:0 !important;padding-top:0 !important}.markdown-body .pl-1{padding-left:4px !important}.markdown-body .pl-2{padding-left:8px !important}.markdown-body .py-2{padding-bottom:8px !important;padding-top:8px !important}.markdown-body .pl-3,.markdown-body .px-3{padding-left:16px !important}.markdown-body .px-3{padding-right:16px !important}.markdown-body .pl-4{padding-left:24px !important}.markdown-body .pl-5{padding-left:32px !important}.markdown-body .pl-6{padding-left:40px !important}.markdown-body .f6{font-size:12px !important}.markdown-body .lh-condensed{line-height:1.25 !important}.markdown-body .text-bold{font-weight:600 !important}.markdown-body:before{content:"";display:table}.markdown-body:after{clear:both;content:"";display:table}.markdown-body>:first-child{margin-top:0 !important}.markdown-body>:last-child{margin-bottom:0 !important}.markdown-body a:not([href]){color:inherit;text-decoration:none}.markdown-body blockquote,.markdown-body dl,.markdown-body ol,.markdown-body p,.markdown-body pre,.markdown-body table,.markdown-body ul{margin-bottom:16px;margin-top:0}.markdown-body hr{background-color:#e1e4e8;border:0;height:.25em;margin:24px 0;padding:0}.markdown-body blockquote{border-left:.25em solid #dfe2e5;color:#6a737d;padding:0 1em}.markdown-body blockquote>:first-child{margin-top:0}.markdown-body blockquote>:last-child{margin-bottom:0}.markdown-body kbd{background-color:#fafbfc;border:1px solid #c6cbd1;border-bottom-color:#959da5;border-radius:3px;box-shadow:inset 0 -1px 0 #959da5;color:#444d56;display:inline-block;font-size:11px;line-height:10px;padding:3px 5px;vertical-align:middle}.markdown-body h1,.markdown-body h2,.markdown-body h3,.markdown-body h4,.markdown-body h5,.markdown-body h6{font-weight:600;line-height:1.25;margin-bottom:16px;margin-top:24px}.markdown-body h1{font-size:2em}.markdown-body h1,.markdown-body h2{padding-bottom:.3em}.markdown-body h2{border-bottom:1px solid #eaecef}.markdown-body h2{font-size:1.5em}.markdown-body h3{font-size:1.25em}.markdown-body h4{font-size:1em}.markdown-body h5{font-size:.875em}.markdown-body h6{color:#6a737d;font-size:.85em}.markdown-body ol,.markdown-body ul{padding-left:2em}.markdown-body ol ol,.markdown-body ol ul,.markdown-body ul ol,.markdown-body ul ul{margin-bottom:0;margin-top:0}.markdown-body li{word-wrap:break-all}.markdown-body li>p{margin-top:16px}.markdown-body li+li{margin-top:.25em}.markdown-body dl{padding:0}.markdown-body dl dt{font-size:1em;font-style:italic;font-weight:600;margin-top:16px;padding:0}.markdown-body dl dd{margin-bottom:16px;padding:0 16px}.markdown-body table{display:block;overflow:auto;width:100%}.markdown-body table th{font-weight:600}.markdown-body table td,.markdown-body table th{border:1px solid #dfe2e5;padding:6px 13px}.markdown-body table tr{background-color:#fff;border-top:1px solid #c6cbd1}.markdown-body table tr:nth-child(2n){background-color:#f6f8fa}.markdown-body img{background-color:#fff;box-sizing:content-box;max-width:100%}.markdown-body img[align=right]{padding-left:20px}.markdown-body img[align=left]{padding-right:20px}.markdown-body code{background-color:rgba(27,31,35,.05);border-radius:3px;font-size:85%;margin:0;padding:.2em .4em}.markdown-body pre{word-wrap:normal}.markdown-body pre>code{background:rgba(0,0,0,0);border:0;font-size:100%;margin:0;padding:0;white-space:pre;word-break:normal;white-space:pre-wrap}.markdown-body .highlight{margin-bottom:16px}.markdown-body .highlight pre{margin-bottom:0;word-break:normal}.markdown-body .highlight pre,.markdown-body pre{background-color:#f6f8fa;border-radius:3px;font-size:85%;line-height:1.45;overflow:auto;padding:16px}.markdown-body pre code{background-color:rgba(0,0,0,0);border:0;display:inline;line-height:inherit;margin:0;max-width:auto;overflow:visible;padding:0;word-wrap:normal}.markdown-body .commit-tease-sha{color:#444d56;display:inline-block;font-family:SFMono-Regular,Consolas,Liberation Mono,Menlo,Courier,monospace;font-size:90%}.markdown-body .blob-wrapper{border-bottom-left-radius:3px;border-bottom-right-radius:3px;overflow-x:auto;overflow-y:hidden}.markdown-body .blob-wrapper-embedded{max-height:240px;overflow-y:auto}.markdown-body .blob-num{-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;color:rgba(27,31,35,.3);cursor:pointer;font-family:SFMono-Regular,Consolas,Liberation Mono,Menlo,Courier,monospace;font-size:12px;line-height:20px;min-width:50px;padding-left:10px;padding-right:10px;text-align:right;user-select:none;vertical-align:top;white-space:nowrap;width:1%}.markdown-body .blob-num:hover{color:rgba(27,31,35,.6)}.markdown-body .blob-num:before{content:attr(data-line-number)}.markdown-body .blob-code{line-height:20px;padding-left:10px;padding-right:10px;position:relative;vertical-align:top}.markdown-body .blob-code-inner{color:#24292e;font-family:SFMono-Regular,Consolas,Liberation Mono,Menlo,Courier,monospace;font-size:12px;overflow:visible;white-space:pre;word-wrap:normal}.markdown-body .pl-token.active,.markdown-body .pl-token:hover{background:#ffea7f;cursor:pointer}.markdown-body kbd{background-color:#fafbfc;border:1px solid #d1d5da;border-bottom-color:#c6cbd1;border-radius:3px;box-shadow:inset 0 -1px 0 #c6cbd1;color:#444d56;display:inline-block;font:11px SFMono-Regular,Consolas,Liberation Mono,Menlo,Courier,monospace;line-height:10px;padding:3px 5px;vertical-align:middle}.markdown-body :checked+.radio-label{border-color:#0366d6;position:relative;z-index:1}.markdown-body .tab-size[data-tab-size="1"]{-moz-tab-size:1;tab-size:1}.markdown-body .tab-size[data-tab-size="2"]{-moz-tab-size:2;tab-size:2}.markdown-body .tab-size[data-tab-size="3"]{-moz-tab-size:3;tab-size:3}.markdown-body .tab-size[data-tab-size="4"]{-moz-tab-size:4;tab-size:4}.markdown-body .tab-size[data-tab-size="5"]{-moz-tab-size:5;tab-size:5}.markdown-body .tab-size[data-tab-size="6"]{-moz-tab-size:6;tab-size:6}.markdown-body .tab-size[data-tab-size="7"]{-moz-tab-size:7;tab-size:7}.markdown-body .tab-size[data-tab-size="8"]{-moz-tab-size:8;tab-size:8}.markdown-body .tab-size[data-tab-size="9"]{-moz-tab-size:9;tab-size:9}.markdown-body .tab-size[data-tab-size="10"]{-moz-tab-size:10;tab-size:10}.markdown-body .tab-size[data-tab-size="11"]{-moz-tab-size:11;tab-size:11}.markdown-body .tab-size[data-tab-size="12"]{-moz-tab-size:12;tab-size:12}.markdown-body .task-list-item{list-style-type:none}.markdown-body .task-list-item+.task-list-item{margin-top:3px}.markdown-body .task-list-item input{margin:0 .2em .25em -1.6em;vertical-align:middle}.markdown-body hr{border-bottom-color:#eee}.markdown-body .pl-0{padding-left:0 !important}.markdown-body .pl-1{padding-left:4px !important}.markdown-body .pl-2{padding-left:8px !important}.markdown-body .pl-3{padding-left:16px !important}.markdown-body .pl-4{padding-left:24px !important}.markdown-body .pl-5{padding-left:32px !important}.markdown-body .pl-6{padding-left:40px !important}.markdown-body .pl-7{padding-left:48px !important}.markdown-body .pl-8{padding-left:64px !important}.markdown-body .pl-9{padding-left:80px !important}.markdown-body .pl-10{padding-left:96px !important}.markdown-body .pl-11{padding-left:112px !important}.markdown-body .pl-12{padding-left:128px !important}.hljs{display:block;overflow-x:auto;padding:.5em;color:#333;background:#f8f8f8}.hljs-comment,.hljs-quote{color:#998;font-style:italic}.hljs-keyword,.hljs-selector-tag,.hljs-subst{color:#333;font-weight:bold}.hljs-number,.hljs-literal,.hljs-variable,.hljs-template-variable,.hljs-tag .hljs-attr{color:teal}.hljs-string,.hljs-doctag{color:#d14}.hljs-title,.hljs-section,.hljs-selector-id{color:#900;font-weight:bold}.hljs-subst{font-weight:normal}.hljs-type,.hljs-class .hljs-title{color:#458;font-weight:bold}.hljs-tag,.hljs-name,.hljs-attribute{color:navy;font-weight:normal}.hljs-regexp,.hljs-link{color:#009926}.hljs-symbol,.hljs-bullet{color:#990073}.hljs-built_in,.hljs-builtin-name{color:#0086b3}.hljs-meta{color:#999;font-weight:bold}.hljs-deletion{background:#fdd}.hljs-addition{background:#dfd}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:bold}:export{mdBreakpoint:576px;smBreakpoint:321px}.auth-page{background:#f3f3f3;text-align:center;min-height:100vh;padding:50px 0}.auth-page .auth-button{margin-top:8px}.auth-page .heading{color:#2a2a2a;font-size:25.6px;font-size:2.56rem;font-weight:300;margin-top:12px;margin-bottom:0}@media(min-width: 576px){.auth-page .heading{font-size:28.8px;font-size:2.88rem}}@media(min-width: 992px){.auth-page .heading{font-size:32px;font-size:3.2rem}}.auth-page .body{max-width:420px;margin-left:auto;margin-right:auto;margin-top:20px}.auth-page .referrer-flash{margin:24px 0}.auth-page .error-flash{margin-bottom:24px}.auth-page .footer{margin-top:20px;line-height:20px}.auth-page .callout{color:#7c7c7c;font-size:14px;font-size:1.4rem}@media(min-width: 576px){.auth-page .callout{font-size:14px;font-size:1.4rem}}@media(min-width: 992px){.auth-page .callout{font-size:14px;font-size:1.4rem}}.auth-page .cta{font-size:14px;font-size:1.4rem}@media(min-width: 576px){.auth-page .cta{font-size:14px;font-size:1.4rem}}@media(min-width: 992px){.auth-page .cta{font-size:14px;font-size:1.4rem}}.auth-page .panel{border:1px solid #d8d8d8;background:#fff;border-radius:2px;padding:20px;text-align:left}.auth-page .auth-button{margin-top:16px}.auth-page .input-row~.input-row{margin-top:12px}.auth-page .label{font-size:14px;font-size:1.4rem;font-weight:600;width:100%;margin-bottom:0}@media(min-width: 576px){.auth-page .label{font-size:14px;font-size:1.4rem}}@media(min-width: 992px){.auth-page .label{font-size:14px;font-size:1.4rem}}.auth-page .forgot{font-size:14px;font-size:1.4rem;float:right;font-weight:400}@media(min-width: 576px){.auth-page .forgot{font-size:14px;font-size:1.4rem}}@media(min-width: 992px){.auth-page .forgot{font-size:14px;font-size:1.4rem}}.auth-page.password-reset-page .email-input{margin-top:1.6rem}.auth-page .alert{margin-bottom:1rem}:export{mdBreakpoint:576px;smBreakpoint:321px}.home-page .note-group-list{flex-grow:1}@media(min-width: 992px){.home-page .note-group-list{margin-top:1.6rem}}.home-page .note-group-list .note-group-list-empty{padding:4rem 1.6rem;text-align:center;color:#686868}.home-page .note-group{position:relative;border-radius:4px;box-shadow:0 0 8px rgba(0,0,0,.14)}.home-page .note-group:not(:first-of-type){margin-top:2rem}@media(min-width: 576px){.home-page .note-group:not(:first-of-type){margin-top:2.4rem}}.home-page .note-group .note-group-header{font-size:14px;font-size:1.4rem;display:flex;justify-content:space-between;color:#fff;padding:1.2rem 1.6rem;background:#f7f9fa;color:#2a2a2a;border-bottom:1px solid #d8d8d8;border-top-left-radius:4px;border-top-right-radius:4px}@media(min-width: 576px){.home-page .note-group .note-group-header{font-size:14px;font-size:1.4rem}}@media(min-width: 992px){.home-page .note-group .note-group-header{font-size:14px;font-size:1.4rem}}.home-page .note-group .date{font-weight:600;font-size:14px;font-size:1.4rem}@media(min-width: 576px){.home-page .note-group .date{font-size:14px;font-size:1.4rem}}@media(min-width: 992px){.home-page .note-group .date{font-size:14px;font-size:1.4rem}}.home-page .note-group .mask{position:absolute;top:0;bottom:0;left:0;right:0;background:#fff;z-index:1;opacity:.8}.home-page .note-group .header-date{font-weight:600;font-size:14.4px;font-size:1.44rem}@media(min-width: 576px){.home-page .note-group .header-date{font-size:14.4px;font-size:1.44rem}}@media(min-width: 992px){.home-page .note-group .header-date{font-size:16px;font-size:1.6rem}}.home-page .note-group .header-count{font-weight:300}.home-page .note-group .list{list-style:none;padding-left:0;margin-bottom:0}.home-page .note-list{list-style:none;padding-left:0;margin-bottom:0}.home-page .note-item{background:#fff;position:relative;border-bottom:1px solid #d8d8d8}.home-page .note-item .link{color:#2a2a2a;display:block;padding:1.2rem 1.6rem;border:2px solid rgba(0,0,0,0)}.home-page .note-item .link:hover{text-decoration:none;background:#ecf4ff;color:inherit}.home-page .note-item .meta{line-height:1.6rem}.home-page .note-item .body{overflow:hidden;text-overflow:ellipsis}.home-page .note-item .note-header{display:flex;justify-content:space-between}.home-page .note-item .note-content{margin-top:1.2rem;line-height:1.6rem;overflow:hidden;text-overflow:ellipsis;color:#686868}.home-page .note-item .book-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:700;font-size:14px;font-size:1.4rem;width:212px}@media(min-width: 576px){.home-page .note-item .book-label{font-size:14px;font-size:1.4rem}}@media(min-width: 992px){.home-page .note-item .book-label{font-size:14px;font-size:1.4rem}}@media(min-width: 576px){.home-page .note-item .book-label{width:320px}}.home-page .note-item .match{display:inline-block;background:#f7f77d;padding:.4rem .4rem}.home-page .toolbar{text-align:right}.home-page .paginator{display:inline-flex;align-items:center}.home-page .paginator .paginator-info{font-size:14px;font-size:1.4rem;color:#686868}@media(min-width: 576px){.home-page .paginator .paginator-info{font-size:14px;font-size:1.4rem}}@media(min-width: 992px){.home-page .paginator .paginator-info{font-size:14px;font-size:1.4rem}}.home-page .paginator .paginator-link{padding:1.2rem 1.2rem}.home-page .paginator .paginator-link.disabled{cursor:not-allowed}.home-page .paginator .paginator-link-prev{margin-left:.8rem}@media(min-width: 576px){.home-page .paginator .paginator-link-prev{margin-left:2rem}}.home-page .paginator .caret-next{transform:rotate(-90deg)}.home-page .paginator .caret-prev{transform:rotate(90deg)}.home-page .paginator .paginator-label{font-weight:600}.note-page{background:#f3f3f3;flex-grow:1;flex-basis:0}.note-page .header{display:flex;align-items:center;justify-content:space-between;padding:1.2rem 1.6rem;border-bottom:1px solid #d8d8d8}.note-page .header-left,.note-page .header-right{display:flex;align-items:center}.note-page .book-icon{vertical-align:middle}.note-page .content-wrapper{padding:1.2rem 1.6rem}.note-page .collapsed-content{color:#8c8c8c}.note-page .footer{display:flex;justify-content:space-between;align-items:center;font-size:14px;font-size:1.4rem;padding:1.2rem 1.6rem}@media(min-width: 576px){.note-page .footer{font-size:14px;font-size:1.4rem}}@media(min-width: 992px){.note-page .footer{font-size:14px;font-size:1.4rem}}.note-page .ts{color:#8c8c8c}.note-page .ts-lead{display:none}@media(min-width: 576px){.note-page .ts-lead{display:inline}}.note-page .match{display:inline-block;background:#f7f77d}.note-page .book-label{font-size:18px;font-size:1.8rem;font-weight:600;display:inline-block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#2a2a2a}@media(min-width: 576px){.note-page .book-label{font-size:18px;font-size:1.8rem}}@media(min-width: 992px){.note-page .book-label{font-size:18px;font-size:1.8rem}}.note-page .book-label a{color:inherit}.note-page .book-label a:hover{color:inherit}.note-page .header .book-label{max-width:20rem;margin-left:1.2rem}@media(min-width: 321px){.note-page .header .book-label{max-width:20rem}}@media(min-width: 576px){.note-page .header .book-label{max-width:42rem}}@media(min-width: 992px){.note-page .header .book-label{max-width:60rem}}.books-page .books-content{padding:1.6rem 2.4rem;margin-top:1.6rem}.books-page .books-content h1{border-bottom:1px solid #f3f3f3;margin-bottom:1.2rem}:export{mdBreakpoint:576px;smBreakpoint:321px}.settings-page .sidebar{box-shadow:0 1px 5px rgba(0,0,0,.2);background:#fff;margin-bottom:2rem;margin-top:2rem}@media(min-width: 992px){.settings-page .sidebar{margin-bottom:0;margin-top:0}}.settings-page .sidebar-item{display:block;padding:1.2rem 1.6rem;border-left:4px solid rgba(0,0,0,0);font-size:14.4px;font-size:1.44rem}@media(min-width: 576px){.settings-page .sidebar-item{font-size:14.4px;font-size:1.44rem}}@media(min-width: 992px){.settings-page .sidebar-item{font-size:16px;font-size:1.6rem}}.settings-page .sidebar-item:hover{text-decoration:none;background:#f7f9fa}.settings-page .sidebar-item.active{font-weight:600;border-left-color:#072a40}@media(min-width: 992px){.settings-page .setting-section-wrapper .header{display:none}}.settings-page .setting-section-wrapper .setting-section{margin-top:2.4rem;background:#fff;box-shadow:0 0 8px rgba(0,0,0,.14)}.settings-page .setting-section-wrapper .setting-section:first-child{margin-top:0}.settings-page .setting-section-wrapper .section-heading{font-size:14.4px;font-size:1.44rem;font-weight:600;padding-bottom:.4rem;background:#f7f9fa;padding:1.6rem 2rem}@media(min-width: 576px){.settings-page .setting-section-wrapper .section-heading{font-size:14.4px;font-size:1.44rem}}@media(min-width: 992px){.settings-page .setting-section-wrapper .section-heading{font-size:16px;font-size:1.6rem}}.settings-page .setting-section-wrapper .section-content{margin-top:2rem}.settings-page .setting-section-wrapper .actions{margin-top:1.8rem;text-align:right}.settings-page .setting-row{padding:1.6rem 2rem}.settings-page .setting-row:not(:last-child){border-bottom:1px solid #d8d8d8}.settings-page .setting-row .setting-row-summary{display:flex;flex-direction:column}@media(min-width: 576px){.settings-page .setting-row .setting-row-summary{flex-direction:row;justify-content:space-between;align-items:center}}.settings-page .setting-row .setting-row-main{padding-top:2.4rem}.settings-page .setting-row .setting-name{font-weight:400;font-size:14.4px;font-size:1.44rem;margin-bottom:0}@media(min-width: 576px){.settings-page .setting-row .setting-name{font-size:14.4px;font-size:1.44rem}}@media(min-width: 992px){.settings-page .setting-row .setting-name{font-size:16px;font-size:1.6rem}}.settings-page .setting-row .setting-desc{margin-bottom:0;font-size:14px;font-size:1.4rem;color:#686868}@media(min-width: 576px){.settings-page .setting-row .setting-desc{font-size:14px;font-size:1.4rem}}@media(min-width: 992px){.settings-page .setting-row .setting-desc{font-size:14px;font-size:1.4rem}}.settings-page .setting-row .setting-action{display:flex;flex-direction:column}@media(min-width: 576px){.settings-page .setting-row .setting-action{flex-direction:row}}.settings-page .setting-row .setting-right{display:flex;word-break:break-all;justify-content:space-between;align-items:center;margin-top:.4rem}@media(min-width: 576px){.settings-page .setting-row .setting-right{flex-direction:row;align-items:center;margin-top:0}}.settings-page .setting-row .setting-edit{color:#6f53c0;padding:0}.settings-page .setting-row .setting-edit:hover{color:#6143b7}@media(min-width: 576px){.settings-page .setting-row .setting-edit{margin-left:1.6rem}}.settings-page .setting-row .input-row~.input-row,.settings-page .setting-row .input-row .input-row{margin-top:1.2rem}.settings-page .email-verification-form{margin-left:1.2rem}:export{mdBreakpoint:576px;smBreakpoint:321px}.header-wrapper{padding:0;z-index:2;position:relative;display:flex;box-shadow:0 1px 5px rgba(0,0,0,.2);background:#072a40;align-items:stretch;justify-content:space-between;flex:1;flex-direction:column;position:sticky;top:0;z-index:4;height:60px}.header-wrapper .container{height:100%}@media(min-width: 576px){.header-wrapper{flex-direction:row}}.header-wrapper .header-content{display:flex;justify-content:space-between;height:100%}.header-wrapper .left{display:flex}.header-wrapper .right{display:flex}.header-wrapper .search-wrapper{align-items:center;display:flex;margin-left:3.2rem}.header-wrapper .search-input{width:35.6rem;border:0;padding:4px 12px;border-radius:.4rem;font-size:14px;font-size:1.4rem}@media(min-width: 576px){.header-wrapper .search-input{font-size:14px;font-size:1.4rem}}@media(min-width: 992px){.header-wrapper .search-input{font-size:14px;font-size:1.4rem}}.header-wrapper .brand{display:flex;align-items:center}.header-wrapper .brand:hover{text-decoration:none}.header-wrapper .main-nav{margin-left:3.2rem;display:flex}.header-wrapper .main-nav .list{display:flex}.header-wrapper .main-nav .item{display:flex;align-items:stretch}.header-wrapper .main-nav .nav-link{font-size:14px;font-size:1.4rem;display:flex;font-weight:600;align-items:center;padding:0 1.6rem;color:#fff}@media(min-width: 576px){.header-wrapper .main-nav .nav-link{font-size:14px;font-size:1.4rem}}@media(min-width: 992px){.header-wrapper .main-nav .nav-link{font-size:14px;font-size:1.4rem}}.header-wrapper .main-nav .nav-link:hover{color:#fff;text-decoration:none;background:#0c486e}.header-wrapper .main-nav .nav-item{font-size:14px;font-size:1.4rem;font-weight:600}@media(min-width: 576px){.header-wrapper .main-nav .nav-item{font-size:14px;font-size:1.4rem}}@media(min-width: 992px){.header-wrapper .main-nav .nav-item{font-size:14px;font-size:1.4rem}}.header-wrapper .dropdown-trigger{color:#fff;padding:16px;font-size:16px;border:none;cursor:pointer}.header-wrapper .dropdown{position:relative;display:inline-block}.header-wrapper .dropdown-content{display:none;position:absolute;background-color:#f1f1f1;width:24rem;background:#fff;border:1px solid #d8d8d8;border-radius:4px;box-shadow:0 0 3px rgba(0,0,0,.15);top:calc(100% + 4px);z-index:1}.header-wrapper .dropdown-content.show{display:block}.header-wrapper .dropdown-content.right-align{right:0}.header-wrapper .account-dropdown .dropdown-trigger{height:100%}.header-wrapper .account-dropdown .account-dropdown-header{font-size:14px;font-size:1.4rem;color:#8c8c8c;padding:.8rem 1.2rem;display:block;margin-bottom:0;white-space:nowrap}@media(min-width: 576px){.header-wrapper .account-dropdown .account-dropdown-header{font-size:14px;font-size:1.4rem}}@media(min-width: 992px){.header-wrapper .account-dropdown .account-dropdown-header{font-size:14px;font-size:1.4rem}}.header-wrapper .account-dropdown .account-dropdown-header svg{fill:#8c8c8c}.header-wrapper .account-dropdown .account-dropdown-header .email{font-weight:600;white-space:normal;word-break:break-all}.header-wrapper .account-dropdown .dropdown-link{font-size:14px;font-size:1.4rem;white-space:pre;padding:.8rem 1.4rem;width:100%;display:block;color:#000}@media(min-width: 576px){.header-wrapper .account-dropdown .dropdown-link{font-size:14px;font-size:1.4rem}}@media(min-width: 992px){.header-wrapper .account-dropdown .dropdown-link{font-size:14px;font-size:1.4rem}}.header-wrapper .account-dropdown .dropdown-link:hover{background:#f3f3f3;text-decoration:none;color:#0056b3}.header-wrapper .account-dropdown .dropdown-link.disabled{color:#d4d4d4;cursor:not-allowed}.header-wrapper .account-dropdown .dropdown-link:not(.disabled):focus{background:#f3f3f3;color:#0056b3;outline:1px dotted gray}.header-wrapper .account-dropdown .session-notice-wrapper{display:flex;align-items:center}.header-wrapper .account-dropdown .session-notice{margin-left:.4rem}.main{position:relative;display:flex;flex-direction:column;background:#f3f3f3;min-height:calc(100vh - 60px)}.main.nofooter{margin-bottom:0}.main.noheader:not(.nofooter){min-height:calc(100vh - 56px)}.main.nofooter:not(.noheader){min-height:calc(100vh - 60px)}.main.nofooter.noheader{min-height:100vh}@media(min-width: 992px){.main{margin-bottom:0;min-height:calc(100vh - 60px)}}.partial--time{color:#686868;font-size:14px;font-size:1.4rem}@media(min-width: 576px){.partial--time{font-size:14px;font-size:1.4rem}}@media(min-width: 992px){.partial--time{font-size:14px;font-size:1.4rem}}@media(min-width: 576px){.partial--time .mobile-text{display:none}}.partial--time .text{display:none}@media(min-width: 576px){.partial--time .text{display:inherit}}@media(min-width: 992px){.partial--page-toolbar{height:4.8rem;border-radius:.4rem;background:#f7f9fa;box-shadow:0 0 8px rgba(0,0,0,.14)}.partial--page-toolbar.bottom{margin-top:1.2rem}}.icon--caret-right{transform:rotate(-90deg)}.icon--caret-left{transform:rotate(90deg)}.frame{box-shadow:0 1px 5px rgba(0,0,0,.2);background:#fff}html{font-size:62.5%}html body{margin:0;font-size:1.6rem}img{max-width:100%}.main-content{padding-top:24px}.no-scroll{overflow:hidden}@media(min-width: 576px)and (max-width: 991px){.container.mobile-nopadding{max-width:100%}}@media(max-width: 991px){.container.mobile-nopadding{padding-left:0;padding-right:0}.container.mobile-nopadding .row{margin-left:0;margin-right:0}.container.mobile-nopadding [class*=col-]{padding-left:0;padding-right:0}}.form-control{font-size:1.6rem}.dropdown{position:inherit}.input-group input~button{border-top-left-radius:0;border-bottom-left-radius:0}.page-bgdark{background:#ececec}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}.input{border-radius:.4rem;background-clip:padding-box;border:1px solid #ced4da;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}/*# sourceMappingURL=main.css.map */ diff --git a/pkg/server/static/main.css.map b/pkg/server/static/main.css.map new file mode 100644 index 00000000..dfbdb1d3 --- /dev/null +++ b/pkg/server/static/main.css.map @@ -0,0 +1 @@ +{"version":3,"sourceRoot":"","sources":["../assets/styles/src/_reboot.scss","../assets/styles/src/_grid.scss","../assets/styles/src/_bootstrap.scss","../assets/styles/src/_variables.scss","../assets/styles/src/_buttons.scss","../assets/styles/src/_font.scss","../assets/styles/src/_responsive.scss","../assets/styles/src/_theme.scss","../assets/styles/src/_select.scss","../assets/styles/src/_shared.scss","../assets/styles/src/_marker.scss","../assets/styles/src/_markdown.scss","../assets/styles/src/_hljs.scss","../assets/styles/src/_login.scss","../assets/styles/src/_home.scss","../assets/styles/src/_note.scss","../assets/styles/src/_books.scss","../assets/styles/src/_settings.scss","../assets/styles/src/_header.scss","../assets/styles/src/_global.scss","../assets/styles/src/main.scss"],"names":[],"mappings":"AAkBA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAOA,qBAGE,sBAGF,KACE,uBACA,iBACA,8BACA,0CAGF,sEAUE,cAGF,KACE,SACA,uLAGA,eACA,gBACA,gBACA,cACA,gBACA,sBAGF,sBACE,qBAGF,GACE,uBACA,SACA,iBAGF,kBAME,aACA,oBAGF,EACE,aACA,mBAGF,sCAEE,0BACA,yCACA,iCACA,YACA,gBACA,sCACA,8BAGF,QACE,mBACA,kBACA,oBAGF,SAGE,aACA,mBAGF,wBAIE,gBAGF,GACE,gBAGF,GACE,oBACA,cAGF,WACE,gBAGF,SAEE,mBAGF,MACE,cAGF,QAEE,kBACA,cACA,cACA,wBAGF,IACE,eAGF,IACE,WAGF,EACE,cACA,qBACA,+BAGF,QACE,cACA,0BAGF,8BACE,cACA,qBAGF,wEAEE,cACA,qBAGF,oCACE,UAGF,kBAIE,2FAEA,cAGF,IACE,aACA,mBACA,cAGF,OACE,gBAGF,IACE,sBACA,kBAGF,IACE,gBACA,sBAGF,MACE,yBAGF,QACE,mBACA,sBACA,cACA,gBACA,oBAGF,GACE,mBAGF,MACE,qBACA,oBAGF,OACE,gBAGF,aACE,mBACA,0CAGF,sCAKE,SACA,oBACA,kBACA,oBAGF,aAEE,iBAGF,cAEE,oBAGF,OACE,iBAGF,gDAIE,0BAGF,4GAIE,eAGF,wHAIE,UACA,kBAGF,uCAEE,sBACA,UAGF,+EAIE,2BAGF,SACE,cACA,gBAGF,SACE,YACA,UACA,SACA,SAGF,OACE,cACA,WACA,eACA,UACA,oBACA,iBACA,oBACA,cACA,mBAGF,SACE,wBAGF,kFAEE,YAGF,cACE,oBACA,wBAGF,yCACE,wBAGF,6BACE,aACA,0BAGF,OACE,qBAGF,QACE,kBACA,eAGF,SACE,aAGF,SACE,wBC1VF;AAAA;AAAA;AAAA;AAAA;AAAA,GAMA,KACE,sBACA,6BAGF,qBAGE,mBAGF,gBACE,WACA,mBACA,kBACA,kBACA,iBAGF,yBACE,gBACE,iBAIJ,yBACE,gBACE,iBAIJ,yBACE,gBACE,iBAIJ,0BACE,gBACE,kBAIJ,0BACE,gBACE,kBAIJ,0BACE,gBACE,kBAIJ,iBACE,WACA,mBACA,kBACA,kBACA,iBAGF,WACE,WACA,mBACA,kBACA,kBACA,iBAGF,yBACE,WACE,iBAIJ,yBACE,WACE,iBAIJ,yBACE,WACE,iBAIJ,0BACE,WACE,kBAIJ,KACE,oBACA,aACA,mBACA,eACA,mBACA,kBAGF,YACE,eACA,cAGF,2CAEE,gBACA,eAGF,sqBAsEE,kBACA,WACA,mBACA,kBAGF,KACE,0BACA,aACA,oBACA,YACA,eAGF,UACE,kBACA,cACA,WACA,eAGF,OACE,uBACA,mBACA,oBAGF,OACE,wBACA,oBACA,qBAGF,OACE,iBACA,aACA,cAGF,OACE,wBACA,oBACA,qBAGF,OACE,wBACA,oBACA,qBAGF,OACE,iBACA,aACA,cAGF,OACE,wBACA,oBACA,qBAGF,OACE,wBACA,oBACA,qBAGF,OACE,iBACA,aACA,cAGF,QACE,wBACA,oBACA,qBAGF,QACE,wBACA,oBACA,qBAGF,QACE,kBACA,cACA,eAGF,aACE,kBACA,SAGF,YACE,kBACA,SAGF,SACE,iBACA,QAGF,SACE,iBACA,QAGF,SACE,iBACA,QAGF,SACE,iBACA,QAGF,SACE,iBACA,QAGF,SACE,iBACA,QAGF,SACE,iBACA,QAGF,SACE,iBACA,QAGF,SACE,iBACA,QAGF,SACE,iBACA,QAGF,UACE,kBACA,SAGF,UACE,kBACA,SAGF,UACE,kBACA,SAGF,UACE,sBAGF,UACE,uBAGF,UACE,gBAGF,UACE,uBAGF,UACE,uBAGF,UACE,gBAGF,UACE,uBAGF,UACE,uBAGF,UACE,gBAGF,WACE,uBAGF,WACE,uBAGF,yBACE,QACE,0BACA,aACA,oBACA,YACA,eAEF,aACE,kBACA,cACA,WACA,eAEF,UACE,uBACA,mBACA,oBAEF,UACE,wBACA,oBACA,qBAEF,UACE,iBACA,aACA,cAEF,UACE,wBACA,oBACA,qBAEF,UACE,wBACA,oBACA,qBAEF,UACE,iBACA,aACA,cAEF,UACE,wBACA,oBACA,qBAEF,UACE,wBACA,oBACA,qBAEF,UACE,iBACA,aACA,cAEF,WACE,wBACA,oBACA,qBAEF,WACE,wBACA,oBACA,qBAEF,WACE,kBACA,cACA,eAEF,gBACE,kBACA,SAEF,eACE,kBACA,SAEF,YACE,iBACA,QAEF,YACE,iBACA,QAEF,YACE,iBACA,QAEF,YACE,iBACA,QAEF,YACE,iBACA,QAEF,YACE,iBACA,QAEF,YACE,iBACA,QAEF,YACE,iBACA,QAEF,YACE,iBACA,QAEF,YACE,iBACA,QAEF,aACE,kBACA,SAEF,aACE,kBACA,SAEF,aACE,kBACA,SAEF,aACE,cAEF,aACE,sBAEF,aACE,uBAEF,aACE,gBAEF,aACE,uBAEF,aACE,uBAEF,aACE,gBAEF,aACE,uBAEF,aACE,uBAEF,aACE,gBAEF,cACE,uBAEF,cACE,wBAIJ,yBACE,QACE,0BACA,aACA,oBACA,YACA,eAEF,aACE,kBACA,cACA,WACA,eAEF,UACE,uBACA,mBACA,oBAEF,UACE,wBACA,oBACA,qBAEF,UACE,iBACA,aACA,cAEF,UACE,wBACA,oBACA,qBAEF,UACE,wBACA,oBACA,qBAEF,UACE,iBACA,aACA,cAEF,UACE,wBACA,oBACA,qBAEF,UACE,wBACA,oBACA,qBAEF,UACE,iBACA,aACA,cAEF,WACE,wBACA,oBACA,qBAEF,WACE,wBACA,oBACA,qBAEF,WACE,kBACA,cACA,eAEF,gBACE,kBACA,SAEF,eACE,kBACA,SAEF,YACE,iBACA,QAEF,YACE,iBACA,QAEF,YACE,iBACA,QAEF,YACE,iBACA,QAEF,YACE,iBACA,QAEF,YACE,iBACA,QAEF,YACE,iBACA,QAEF,YACE,iBACA,QAEF,YACE,iBACA,QAEF,YACE,iBACA,QAEF,aACE,kBACA,SAEF,aACE,kBACA,SAEF,aACE,kBACA,SAEF,aACE,cAEF,aACE,sBAEF,aACE,uBAEF,aACE,gBAEF,aACE,uBAEF,aACE,uBAEF,aACE,gBAEF,aACE,uBAEF,aACE,uBAEF,aACE,gBAEF,cACE,uBAEF,cACE,wBAIJ,yBACE,QACE,0BACA,aACA,oBACA,YACA,eAEF,aACE,kBACA,cACA,WACA,eAEF,UACE,uBACA,mBACA,oBAEF,UACE,wBACA,oBACA,qBAEF,UACE,iBACA,aACA,cAEF,UACE,wBACA,oBACA,qBAEF,UACE,wBACA,oBACA,qBAEF,UACE,iBACA,aACA,cAEF,UACE,wBACA,oBACA,qBAEF,UACE,wBACA,oBACA,qBAEF,UACE,iBACA,aACA,cAEF,WACE,wBACA,oBACA,qBAEF,WACE,wBACA,oBACA,qBAEF,WACE,kBACA,cACA,eAEF,gBACE,kBACA,SAEF,eACE,kBACA,SAEF,YACE,iBACA,QAEF,YACE,iBACA,QAEF,YACE,iBACA,QAEF,YACE,iBACA,QAEF,YACE,iBACA,QAEF,YACE,iBACA,QAEF,YACE,iBACA,QAEF,YACE,iBACA,QAEF,YACE,iBACA,QAEF,YACE,iBACA,QAEF,aACE,kBACA,SAEF,aACE,kBACA,SAEF,aACE,kBACA,SAEF,aACE,cAEF,aACE,sBAEF,aACE,uBAEF,aACE,gBAEF,aACE,uBAEF,aACE,uBAEF,aACE,gBAEF,aACE,uBAEF,aACE,uBAEF,aACE,gBAEF,cACE,uBAEF,cACE,wBAIJ,0BACE,QACE,0BACA,aACA,oBACA,YACA,eAEF,aACE,kBACA,cACA,WACA,eAEF,UACE,uBACA,mBACA,oBAEF,UACE,wBACA,oBACA,qBAEF,UACE,iBACA,aACA,cAEF,UACE,wBACA,oBACA,qBAEF,UACE,wBACA,oBACA,qBAEF,UACE,iBACA,aACA,cAEF,UACE,wBACA,oBACA,qBAEF,UACE,wBACA,oBACA,qBAEF,UACE,iBACA,aACA,cAEF,WACE,wBACA,oBACA,qBAEF,WACE,wBACA,oBACA,qBAEF,WACE,kBACA,cACA,eAEF,gBACE,kBACA,SAEF,eACE,kBACA,SAEF,YACE,iBACA,QAEF,YACE,iBACA,QAEF,YACE,iBACA,QAEF,YACE,iBACA,QAEF,YACE,iBACA,QAEF,YACE,iBACA,QAEF,YACE,iBACA,QAEF,YACE,iBACA,QAEF,YACE,iBACA,QAEF,YACE,iBACA,QAEF,aACE,kBACA,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,kDACE,aAEF,qBACE,eACA,mBACA,kBACA,kBACA,sBACA,WAEF,gBACE,8DACA,yDACA,sDACA,WACA,YACA,sBACA,kBACA,sBACA,wBACA,qBACA,kBACA,sBAEF,mBACE,gDACA,2CACA,wCACA,WACA,eACA,mBACA,kBACA,kBACA,sBACA,WAEF,yBACE,cAEF,cACE,qBACA,eACA,cAEF,kCACE,WAEF,mBACE,eACA,mBACA,kBACA,kBACA,sBACA,WACA,kBAEF,gCACE,gBACA,iBAEF,cACE,8CACA,mBACA,2BACA,qBACA,SACA,QACA,kBAEF,6BACE,kBAEF,2CACE,qBAEF,0BACE,kBACA,qBACA,WACA,UACA,YACA,sBACA,gBACA,WAEF,2CACE,KACE,UAEF,GACE,WAGJ,mCACE,KACE,UAEF,GACE,WAGJ,mBACE,+BACA,8BACA,sBACA,sBACA,yBACA,mCACA,sBACA,gBACA,iBACA,kBACA,OACA,SACA,WACA,UACA,iCAEF,mCACE,yBAEF,aACE,iBACA,gBAEF,eACE,sBACA,sBACA,WACA,eACA,cACA,iBAEF,0BACE,+BACA,8BAEF,2BACE,yBAEA,qCACA,WAEF,0BACE,yBAEA,qCACA,WAEF,2BACE,WACA,eAEF,kBACE,sBACA,WACA,eACA,cACA,iBAEF,6BACE,sBACA,iBACA,UAEF,yCACE,cACA,kBAEF,uCACE,gBAEF,6BACE,yBAEA,qCACA,kBACA,yBAEA,qCACA,cACA,qBACA,eACA,gBACA,gBACA,eACA,mBAEF,qEAEE,qBACA,sBAEF,mCACE,+BACA,4BACA,eACA,gBAEF,oCACE,cACA,eACA,qBAEF,0CACE,0BAEF,kCACE,eACA,8BACA,2BACA,+BAEA,2CACA,oBAEF,gFAEE,yBAEA,qCACA,cAEF,yCACE,yBAEA,qCAEF,yCACE,cACA,iBAEF,8CACE,kBACA,8BAEA,0CAEF,yCACE,yBACA,yBACA,WAEF,8CACE,mBACA,+BAEF,6JAGE,yBAEF,iCACE,GACE,yBAGJ,yCACE,GACE,iCLjbJ,QACE,aAJc,MAKd,aAJc,MAEhB,QACE,aAJc,MAKd,aAJc,MMJhB,uBACE,GACE,WAEF,IACE,UAEF,KACE,YAKJ,QACE,qCACA,mBAEA,oBACE,mBAIJ,iIAKE,iBFxBa,QEyBb,mBAGF,eACE,gBACA,eACA,gBAGF,SACE,aAGF,mBACE,kBACA,YACA,WACA,YACA,gBAIA,sBAEE,cAIJ,YACE,yBACA,qBACA,kBACA,oBACA,cAEA,yBACE,MF/DG,QEiEL,kBACE,aF7CS,QE8CT,6EAEA,aAIJ,kBACE,qBAGF,mBACE,qBAGF,oBACE,WAGF,YACE,WAGF,EACE,MF5EK,QE8EL,QACE,MF9ES,QEmFb,kBAME,gBH5EE,yBGgFJ,qBAEI,gBHlFA,yBGqFJ,4BAEI,eACA,gBAEA,iCACE,cACA,eAEF,0CAEE,eACA,iBAIN,UACE,kBAGF,MACE,iBACA,oBAEA,uBACE,cACA,iBHvIA,yBGqIF,uBAKI,mBACA,uBAKN,aACE,gBAEA,8BACE,mBHpJA,yBGgJJ,aASI,mBACA,cAIJ,aACE,gBACA,mZACA,sBACA,4BACA,qCACA,yBACA,yBACA,gBACA,gBACA,mBACA,aACA,sBACA,kBACA,8CAEA,mBACE,qBACA,aACA,2EAGF,wDAEE,oiBACA,iBFzLW,QE6Lf,aAEE,WACA,gBACA,oBJ3HE,eACA,iBCjEA,yBGuLJ,aJnHM,eACA,kBCzEF,yBG2LJ,aJ9GM,eACA,kBIqHN,cJ/HI,iBACA,kBCjEA,yBG+LJ,cJ3HM,iBACA,mBCzEF,yBGmMJ,cJtHM,eACA,kBIyHN,gBACE,qBACA,sBACA,qBACA,uBACA,qCACA,oCACA,oCACA,kBAGF,SACE,SACA,gBACA,6BC7NF,QACE,qBACA,mBACA,cACA,gBACA,cACA,kBACA,mBACA,wBACA,qBAGF,cACE,WACA,yBAGF,aACE,WACA,yBCPF,uBACE,WACA,cACA,kBACA,kBAGF,6BACE,aAGF,gMAME,qBAGF,eACE,0BACA,8BACA,cACA,gBACA,kIAEA,eACA,gBACA,qBAGF,qBACE,cAGF,iDAEE,cAGF,2CAEE,cAGF,mDAEE,cAGF,uBACE,cAGF,qBACE,cAGF,gMAOE,cAGF,4CAEE,cAGF,sBACE,cAGF,sBACE,yBACA,cAGF,sBACE,yBACA,cAGF,6BACE,aAGF,8BACE,cACA,gBAGF,sBACE,cAGF,yEAGE,cACA,gBAGF,sBACE,cACA,kBAGF,sBACE,cACA,gBAGF,sBACE,yBACA,cAGF,uBACE,yBACA,cAGF,sBACE,yBACA,cAGF,uBACE,yBACA,cAGF,uBACE,cACA,gBAGF,sBACE,cAGF,sBACE,cAGF,wBACE,cACA,0BAGF,uBACE,cAGF,uBACE,kBAGF,iBACE,+BAGF,+CAEE,gBAGF,sBACE,oBACA,mBAGF,kBACE,cACA,eAGF,mBACE,kBAGF,0DAGE,gCACA,cAGF,kBACE,uBACA,SACA,iBAGF,qBACE,aACA,SAGF,qBACE,iBAGF,+BACE,sBACA,UAGF,iBACE,sBAGF,qBACE,oBACA,kBACA,oBAGF,iBACE,cACA,qBAGF,uBACE,0BAGF,sBACE,gBAGF,kBACE,yBACA,SACA,gCACA,SACA,cACA,gBAGF,yBACE,WACA,cAGF,wBACE,WACA,WACA,cAGF,qBACE,yBACA,iBAGF,oCAEE,UAGF,+BACE,eAGF,4GAME,gBACA,aAGF,kBACE,eAGF,oCAEE,gBAGF,kBACE,eAGF,kBACE,eAGF,oCAEE,gBAGF,kBACE,eAGF,kBACE,eAGF,oCAEE,gBAGF,kBACE,eAGF,iBACE,mBACA,aAGF,0BACE,SAGF,oCAEE,gBACA,aACA,eAGF,0CAEE,4BAGF,gGAIE,4BAGF,kBACE,cAGF,uCAEE,4EAEA,eAGF,mBACE,gBACA,aAGF,gGAEE,wBACA,gBACA,SAGF,uBACE,oCAGF,yBACE,oBAGF,8BACE,2CAGF,0BACE,6BAGF,yBACE,iCAGF,8BACE,oCAGF,gCACE,yBAGF,qBACE,2BAGF,qBACE,6BACA,0BAGF,qBACE,0BAGF,qBACE,4BACA,yBAGF,qBACE,4BAGF,qBACE,4BAGF,qBACE,8BACA,2BAGF,0CAEE,6BAGF,qBACE,8BAGF,qBACE,6BAGF,qBACE,6BAGF,qBACE,6BAGF,mBACE,0BAGF,6BACE,4BAGF,0BACE,2BAGF,sBACE,WACA,cAGF,qBACE,WACA,WACA,cAGF,4BACE,wBAGF,2BACE,2BAGF,6BACE,cACA,qBAGF,yIAOE,mBACA,aAGF,kBACE,yBACA,SACA,aACA,cACA,UAGF,0BACE,gCACA,cACA,cAGF,uCACE,aAGF,sCACE,gBAGF,mBACE,yBACA,yBACA,4BACA,kBACA,kCACA,cACA,qBACA,eACA,iBACA,gBACA,sBAGF,4GAME,gBACA,iBACA,mBACA,gBAGF,kBACE,cAGF,oCAEE,oBAGF,kBACE,gCAGF,kBACE,gBAGF,kBACE,iBAGF,kBACE,cAGF,kBACE,iBAGF,kBACE,cACA,gBAGF,oCAEE,iBAGF,oFAIE,gBACA,aAGF,kBACE,oBAGF,oBACE,gBAGF,qBACE,iBAGF,kBACE,UAGF,qBACE,cACA,kBACA,gBACA,gBACA,UAGF,qBACE,mBACA,eAGF,qBACE,cACA,cACA,WAGF,wBACE,gBAGF,gDAEE,yBACA,iBAGF,wBACE,sBACA,6BAGF,sCACE,yBAGF,mBACE,sBACA,uBACA,eAGF,gCACE,kBAGF,+BACE,mBAGF,oBACE,oCACA,kBACA,cACA,SACA,kBAGF,mBACE,iBAGF,wBACE,yBACA,SACA,eACA,SACA,UACA,gBACA,kBACA,qBAGF,0BACE,mBAGF,8BACE,gBACA,kBAGF,iDAEE,yBACA,kBACA,cACA,iBACA,cACA,aAGF,wBACE,+BACA,SACA,eACA,oBACA,SACA,eACA,iBACA,UACA,iBAGF,iCACE,cACA,qBACA,4EAEA,cAGF,6BACE,8BACA,+BACA,gBACA,kBAGF,sCACE,iBACA,gBAGF,yBACE,sBACA,qBACA,yBACA,wBACA,eACA,4EAEA,eACA,iBACA,eACA,kBACA,mBACA,iBACA,iBACA,mBACA,mBACA,SAGF,+BACE,wBAGF,gCACE,+BAGF,0BACE,iBACA,kBACA,mBACA,kBACA,mBAGF,gCACE,cACA,4EAEA,eACA,iBACA,gBACA,iBAGF,+DAEE,mBACA,eAGF,mBACE,yBACA,yBACA,4BACA,kBACA,kCACA,cACA,qBACA,0EAEA,iBACA,gBACA,sBAGF,qCACE,qBACA,kBACA,UAGF,4C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","file":"main.css"} \ No newline at end of file diff --git a/pkg/server/testutils/main.go b/pkg/server/testutils/main.go index 018b95c4..fd97c1d9 100644 --- a/pkg/server/testutils/main.go +++ b/pkg/server/testutils/main.go @@ -25,6 +25,9 @@ import ( "fmt" "math/rand" "net/http" + "net/url" + "reflect" + // "strconv" "strings" "sync" "testing" @@ -198,6 +201,7 @@ func MakeReq(endpoint string, method, path, data string) *http.Request { u := fmt.Sprintf("%s%s", endpoint, path) req, err := http.NewRequest(method, u, strings.NewReader(data)) + if err != nil { panic(errors.Wrap(err, "constructing http request")) } @@ -205,6 +209,14 @@ func MakeReq(endpoint string, method, path, data string) *http.Request { return req } +// MakeFormReq makes an HTTP request and returns a response +func MakeFormReq(endpoint, method, path string, data url.Values) *http.Request { + req := MakeReq(endpoint, method, path, data.Encode()) + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + + return req +} + // MustExec fails the test if the given database query has error func MustExec(t *testing.T, db *gorm.DB, message string) { if err := db.Error; err != nil { @@ -271,3 +283,71 @@ func (b *MockEmailbackendImplementation) Queue(subject, from string, to []string return nil } + +// EndpointType is the type of endpoint to be tested +type EndpointType int + +const ( + // EndpointWeb represents a web endpoint returning HTML + EndpointWeb EndpointType = iota + // EndpointAPI represents an API endpoint returning JSON + EndpointAPI +) + +type endpointTest func(t *testing.T, target EndpointType) + +// RunForWebAndAPI runs the given test function for web and API +func RunForWebAndAPI(t *testing.T, name string, runTest endpointTest) { + t.Run(fmt.Sprintf("%s-web", name), func(t *testing.T) { + runTest(t, EndpointWeb) + }) + + t.Run(fmt.Sprintf("%s-api", name), func(t *testing.T) { + runTest(t, EndpointAPI) + }) +} + +// PayloadWrapper is a wrapper for a payload that can be converted to +// either URL form values or JSON +type PayloadWrapper struct { + Data interface{} +} + +func (p PayloadWrapper) ToURLValues() url.Values { + values := url.Values{} + + el := reflect.ValueOf(p.Data) + if el.Kind() == reflect.Ptr { + el = el.Elem() + } + iVal := el + typ := iVal.Type() + for i := 0; i < iVal.NumField(); i++ { + fi := typ.Field(i) + name := fi.Tag.Get("schema") + if name == "" { + name = fi.Name + } + + if !iVal.Field(i).IsNil() { + values.Set(name, fmt.Sprint(iVal.Field(i).Elem())) + } + } + + return values +} + +func (p PayloadWrapper) ToJSON(t *testing.T) string { + b, err := json.Marshal(p.Data) + if err != nil { + t.Fatal(err) + } + + return string(b) +} + +// TrueVal is a true value +var TrueVal = true + +// FalseVal is a false value +var FalseVal = false diff --git a/pkg/server/tmpl/data.go b/pkg/server/tmpl/data.go index bee3d785..321c3697 100644 --- a/pkg/server/tmpl/data.go +++ b/pkg/server/tmpl/data.go @@ -28,7 +28,7 @@ import ( "time" "github.com/dnote/dnote/pkg/server/database" - "github.com/dnote/dnote/pkg/server/handlers" + "github.com/dnote/dnote/pkg/server/middleware" "github.com/dnote/dnote/pkg/server/operations" "github.com/pkg/errors" ) @@ -52,12 +52,12 @@ type notePage struct { } func (a AppShell) newNotePage(r *http.Request, noteUUID string) (notePage, error) { - user, _, err := handlers.AuthWithSession(a.DB, r, nil) + user, _, err := middleware.AuthWithSession(a.DB, r) if err != nil { return notePage{}, errors.Wrap(err, "authenticating with session") } - note, ok, err := operations.GetNote(a.DB, noteUUID, user) + note, ok, err := operations.GetNote(a.DB, noteUUID, &user) if !ok { return notePage{}, ErrNotFound diff --git a/pkg/server/views/books/index.gohtml b/pkg/server/views/books/index.gohtml new file mode 100644 index 00000000..46ebbecc --- /dev/null +++ b/pkg/server/views/books/index.gohtml @@ -0,0 +1,20 @@ +{{define "yield"}} +
+ + + +
+ +
+
+{{end}} diff --git a/pkg/server/views/books/show.gohtml b/pkg/server/views/books/show.gohtml new file mode 100644 index 00000000..4d84d095 --- /dev/null +++ b/pkg/server/views/books/show.gohtml @@ -0,0 +1,4 @@ +{{define "yield"}} + content + {{ .Note.Body }} +{{end}} diff --git a/pkg/server/views/data.go b/pkg/server/views/data.go new file mode 100644 index 00000000..d36609f5 --- /dev/null +++ b/pkg/server/views/data.go @@ -0,0 +1,152 @@ +package views + +import ( + "net/http" + "time" + + "github.com/dnote/dnote/pkg/server/database" + "github.com/pkg/errors" +) + +const ( + // AlertLvlError is an alert level for error + AlertLvlError = "danger" + // AlertLvlWarning is an alert level for warning + AlertLvlWarning = "warning" + // AlertLvlInfo is an alert level for info + AlertLvlInfo = "info" + // AlertLvlSuccess is an alert level for success + AlertLvlSuccess = "success" + + // AlertMsgGeneric is a generic message for a server error + AlertMsgGeneric = "Something went wrong. Please try again." +) + +// Alert is used to render Bootstrap Alert messages in templates +type Alert struct { + Level string + Message string +} + +// Data is the top level structure that views expect data to come in. +type Data struct { + Alert *Alert + // CSRF template.HTML + User *database.User + Account *database.Account + Yield map[string]interface{} +} + +func getErrMessage(err error) string { + if pErr, ok := err.(PublicError); ok { + return pErr.Public() + } + + return AlertMsgGeneric +} + +// PutAlert puts an alert in the given data. +func (d *Data) PutAlert(alert Alert, alertInYield bool) { + if alertInYield { + if d.Yield == nil { + d.Yield = map[string]interface{}{} + } + d.Yield["Alert"] = &alert + } else { + d.Alert = &alert + } +} + +// SetAlert sets alert in the given data for given error. +func (d *Data) SetAlert(err error, alertInYield bool) { + errC := errors.Cause(err) + + var alert Alert + if pErr, ok := errC.(PublicError); ok { + alert = Alert{ + Level: AlertLvlError, + Message: pErr.Public(), + } + } else { + alert = Alert{ + Level: AlertLvlError, + Message: AlertMsgGeneric, + } + } + + d.PutAlert(alert, alertInYield) +} + +// AlertError returns a new error alert using the given message. +func (d *Data) AlertError(msg string) { + d.Alert = &Alert{ + Level: AlertLvlError, + Message: msg, + } +} + +func persistAlert(w http.ResponseWriter, alert Alert) { + expiresAt := time.Now().Add(5 * time.Minute) + lvl := http.Cookie{ + Name: "alert_level", + Value: alert.Level, + Expires: expiresAt, + Path: "/", + HttpOnly: true, + } + msg := http.Cookie{ + Name: "alert_message", + Value: alert.Message, + Expires: expiresAt, + Path: "/", + HttpOnly: true, + } + http.SetCookie(w, &lvl) + http.SetCookie(w, &msg) +} + +func clearAlert(w http.ResponseWriter) { + lvl := http.Cookie{ + Name: "alert_level", + Value: "", + Expires: time.Now(), + HttpOnly: true, + } + msg := http.Cookie{ + Name: "alert_message", + Value: "", + Expires: time.Now(), + HttpOnly: true, + } + http.SetCookie(w, &lvl) + http.SetCookie(w, &msg) +} + +func getAlert(r *http.Request) *Alert { + lvl, err := r.Cookie("alert_level") + if err != nil { + return nil + } + msg, err := r.Cookie("alert_message") + if err != nil { + return nil + } + alert := Alert{ + Level: lvl.Value, + Message: msg.Value, + } + return &alert +} + +// RedirectAlert redirects to a URL after persisting the provided alert data +// into a cookie so that it can be displayed when the page is rendered. +func RedirectAlert(w http.ResponseWriter, r *http.Request, urlStr string, code int, alert Alert) { + persistAlert(w, alert) + http.Redirect(w, r, urlStr, code) +} + +// PublicError is an error meant to be displayed to the public +type PublicError interface { + error + Public() string +} diff --git a/pkg/server/views/helpers.go b/pkg/server/views/helpers.go new file mode 100644 index 00000000..ee82eb4f --- /dev/null +++ b/pkg/server/views/helpers.go @@ -0,0 +1,176 @@ +package views + +import ( + "fmt" + "strconv" + "strings" + "time" + + "github.com/dnote/dnote/pkg/server/app" + "github.com/dnote/dnote/pkg/server/buildinfo" + "github.com/pkg/errors" + "html/template" +) + +func initHelpers(c Config, a *app.App) template.FuncMap { + ctx := newViewCtx(c) + + ret := template.FuncMap{ + "csrfField": ctx.csrfField, + "css": ctx.css, + "js": ctx.js, + "title": ctx.title, + "headerTemplate": ctx.headerTemplate, + "rootURL": ctx.rootURL, + "getFullMonthName": ctx.getFullMonthName, + "toDateTime": ctx.toDateTime, + "excerpt": ctx.excerpt, + "timeAgo": ctx.timeAgo, + "timeFormat": ctx.timeFormat, + "toISOString": ctx.toISOString, + "dict": ctx.dict, + "defaultValue": ctx.defaultValue, + "add": ctx.add, + "assetBaseURL": func() string { + return a.Config.AssetBaseURL + }, + } + + // extend with helpers that are defined specific to a view + if c.HelperFuncs != nil { + for k, v := range c.HelperFuncs { + ret[k] = v + } + } + + return ret +} + +func (v viewCtx) csrfField() (template.HTML, error) { + return "", errors.New("csrfField is not implemented") +} + +func (v viewCtx) css() []string { + return strings.Split(buildinfo.CSSFiles, ",") +} + +func (v viewCtx) js() []string { + return strings.Split(buildinfo.JSFiles, ",") +} + +func (v viewCtx) title() string { + if v.Config.Title != "" { + return fmt.Sprintf("%s | %s", v.Config.Title, siteTitle) + } + + return siteTitle +} + +func (v viewCtx) headerTemplate() string { + return v.Config.HeaderTemplate +} + +func (v viewCtx) toDateTime(year, month int) string { + sb := strings.Builder{} + + sb.WriteString(strconv.Itoa(year)) + sb.WriteString("-") + + if month < 10 { + sb.WriteString("0") + sb.WriteString(strconv.Itoa(month)) + } else { + sb.WriteString(strconv.Itoa(month)) + } + + return sb.String() +} + +func (v viewCtx) getFullMonthName(month int) string { + return time.Month(month).String() +} + +func (v viewCtx) rootURL() string { + return buildinfo.RootURL +} + +func min(a, b int) int { + if a < b { + return a + } + + return b +} + +func max(a, b int) int { + if a > b { + return a + } + + return b +} + +// excerpt trims the given string up to the last word that makes the string +// exceed the maxLength, and attaches ellipses at the end. If the string is +// shorter than the given maxLength, it returns the original string. +func (v viewCtx) excerpt(s string, maxLength int) string { + if len(s) < maxLength { + return s + } + + ret := s[0:maxLength] + ret = s[0:min(len(ret), max(0, strings.LastIndex(ret, " ")))] + ret += "..." + + return ret +} + +func (v viewCtx) timeFormat(t time.Time, format string) string { + return t.Format(format) +} + +func (v viewCtx) timeAgo(t time.Time) string { + now := v.Clock.Now() + diff := relativeTimeDiff(now, t) + + if diff.tense == "past" { + return fmt.Sprintf("%s ago", diff.text) + } + + if diff.tense == "future" { + return fmt.Sprintf("in %s", diff.text) + } + + return diff.text +} + +func (v viewCtx) toISOString(t time.Time) string { + return t.Format(time.RFC3339) +} + +func (v viewCtx) dict(values ...interface{}) (map[string]interface{}, error) { + if len(values)%2 != 0 { + return nil, errors.New("invalid dict call") + } + dict := make(map[string]interface{}, len(values)/2) + for i := 0; i < len(values); i += 2 { + key, ok := values[i].(string) + if !ok { + return nil, errors.New("dict keys must be strings") + } + dict[key] = values[i+1] + } + return dict, nil +} + +func (v viewCtx) defaultValue(value, fallback interface{}) interface{} { + if value == nil { + return fallback + } + + return value +} + +func (v viewCtx) add(a, b int) interface{} { + return a + b +} diff --git a/pkg/server/views/helpers_test.go b/pkg/server/views/helpers_test.go new file mode 100644 index 00000000..d469a9fc --- /dev/null +++ b/pkg/server/views/helpers_test.go @@ -0,0 +1,183 @@ +package views + +import ( + "fmt" + "testing" + "time" + + "github.com/dnote/dnote/pkg/assert" +) + +func TestToDateTime(t *testing.T) { + testCases := []struct { + year int + month int + expected string + }{ + { + year: 2010, + month: 10, + expected: "2010-10", + }, + { + year: 2010, + month: 8, + expected: "2010-08", + }, + } + + ctx := viewCtx{} + + for _, tc := range testCases { + got := ctx.toDateTime(tc.year, tc.month) + + assert.Equal(t, got, tc.expected, "result mismatch") + } +} + +func TestGetFullMonthName(t *testing.T) { + testCases := []struct { + input int + expected string + }{ + { + input: 1, + expected: "January", + }, + { + input: 12, + expected: "December", + }, + } + + ctx := viewCtx{} + + for _, tc := range testCases { + got := ctx.getFullMonthName(tc.input) + + assert.Equal(t, got, tc.expected, "result mismatch") + } +} + +func TestExcerpt(t *testing.T) { + testCases := []struct { + str string + maxLength int + expected string + }{ + { + str: "hello world", + maxLength: 5, + expected: "...", + }, + { + str: "hello world", + maxLength: 1, + expected: "...", + }, + { + str: "hello world", + maxLength: 7, + expected: "hello...", + }, + { + str: "foo bar baz", + maxLength: 9, + expected: "foo bar...", + }, + { + str: "foo", + maxLength: 4, + expected: "foo", + }, + } + + ctx := viewCtx{} + + for idx, tc := range testCases { + got := ctx.excerpt(tc.str, tc.maxLength) + assert.Equal(t, got, tc.expected, fmt.Sprintf("result mismatch for case %d", idx)) + } +} + +func TestTimeAgo(t *testing.T) { + now := time.Now() + + testCases := []struct { + input time.Time + expected string + }{ + { + input: now.Add(-2 * time.Hour), + expected: "2 hours ago", + }, + { + input: now.Add(-2*time.Hour - 59*time.Minute), + expected: "2 hours ago", + }, + { + input: now.Add(-23 * time.Hour), + expected: "23 hours ago", + }, + { + input: now.Add(-23*time.Hour - 59*time.Minute), + expected: "23 hours ago", + }, + { + input: now.Add(-24 * time.Hour), + expected: "1 day ago", + }, + { + input: now.Add(-47 * time.Hour), + expected: "1 day ago", + }, + { + input: now.Add(-48 * time.Hour), + expected: "2 days ago", + }, + + { + input: now.Add(-24 * time.Hour * 7), + expected: "1 week ago", + }, + { + input: now.Add(-24 * time.Hour * 7 * 2), + expected: "2 weeks ago", + }, + + { + input: now.Add(-24 * time.Hour * 7 * 4), + expected: "1 month ago", + }, + { + input: now.Add(-24 * time.Hour * 7 * 7), + expected: "1 month ago", + }, + { + input: now.Add(-24 * time.Hour * 7 * 8), + expected: "2 months ago", + }, + + { + input: now.Add(-24 * time.Hour * 7 * 52), + expected: "1 year ago", + }, + { + input: now.Add(-24 * time.Hour * 7 * 55), + expected: "1 year ago", + }, + { + input: now.Add(-24 * time.Hour * 7 * 52 * 2), + expected: "2 years ago", + }, + } + + ctx := newViewCtx(Config{}) + + for _, tc := range testCases { + t.Run(fmt.Sprintf("input %s", tc.input.String()), func(t *testing.T) { + got := ctx.timeAgo(tc.input) + assert.Equal(t, got, tc.expected, "result mismatch") + }) + } +} diff --git a/pkg/server/views/icons/book.gohtml b/pkg/server/views/icons/book.gohtml new file mode 100644 index 00000000..a04a3e68 --- /dev/null +++ b/pkg/server/views/icons/book.gohtml @@ -0,0 +1,17 @@ +{{define "book"}} + + Book + Icon depicting a book + + +{{end}} diff --git a/pkg/server/views/icons/caret.gohtml b/pkg/server/views/icons/caret.gohtml new file mode 100644 index 00000000..0a9f60a0 --- /dev/null +++ b/pkg/server/views/icons/caret.gohtml @@ -0,0 +1,26 @@ +{{define "caret"}} + + + + +{{end}} diff --git a/pkg/server/views/icons/lock.gohtml b/pkg/server/views/icons/lock.gohtml new file mode 100644 index 00000000..f43c202a --- /dev/null +++ b/pkg/server/views/icons/lock.gohtml @@ -0,0 +1,10 @@ +{{define "lockIcon"}} + + + +{{end}} diff --git a/pkg/server/views/icons/logo.gohtml b/pkg/server/views/icons/logo.gohtml new file mode 100644 index 00000000..fac485e5 --- /dev/null +++ b/pkg/server/views/icons/logo.gohtml @@ -0,0 +1,14 @@ +{{define "logo"}} + + + +{{end}} diff --git a/pkg/server/views/icons/logo_with_text.gohtml b/pkg/server/views/icons/logo_with_text.gohtml new file mode 100644 index 00000000..7f9d7c66 --- /dev/null +++ b/pkg/server/views/icons/logo_with_text.gohtml @@ -0,0 +1,26 @@ +{{define "logoWithText"}} + + Dnote logo + Dnote logo + + + + + + + + + + +{{end}} diff --git a/pkg/server/views/layouts/alert.gohtml b/pkg/server/views/layouts/alert.gohtml new file mode 100644 index 00000000..661753f9 --- /dev/null +++ b/pkg/server/views/layouts/alert.gohtml @@ -0,0 +1,9 @@ +{{define "alert"}} +{{if .}} + +{{end}} +{{end}} diff --git a/pkg/server/views/layouts/base.gohtml b/pkg/server/views/layouts/base.gohtml new file mode 100644 index 00000000..fe6ace0b --- /dev/null +++ b/pkg/server/views/layouts/base.gohtml @@ -0,0 +1,40 @@ +{{define "base"}} + + + + + + {{ title }} + + + + + + + + + + + + + + + + + + {{template "css" .}} + + + + {{template "header" .}} + + {{template "alert" .Alert}} + +
+ {{template "yield" .Yield}} +
+ + {{template "js" .}} + + +{{end}} diff --git a/pkg/server/views/layouts/css.gohtml b/pkg/server/views/layouts/css.gohtml new file mode 100644 index 00000000..4d248a56 --- /dev/null +++ b/pkg/server/views/layouts/css.gohtml @@ -0,0 +1,5 @@ +{{define "css"}} + {{range css}} + + {{end}} +{{end}} diff --git a/pkg/server/views/layouts/header.gohtml b/pkg/server/views/layouts/header.gohtml new file mode 100644 index 00000000..6d8451e7 --- /dev/null +++ b/pkg/server/views/layouts/header.gohtml @@ -0,0 +1,7 @@ +{{define "header"}} + +{{if eq headerTemplate "navbar"}} + {{ template "navbar" . }} +{{end}} + +{{end}} diff --git a/pkg/server/views/layouts/js.gohtml b/pkg/server/views/layouts/js.gohtml new file mode 100644 index 00000000..50b23673 --- /dev/null +++ b/pkg/server/views/layouts/js.gohtml @@ -0,0 +1,5 @@ +{{define "js"}} + {{range js}} + + {{end}} +{{end}} diff --git a/pkg/server/views/layouts/navbar.gohtml b/pkg/server/views/layouts/navbar.gohtml new file mode 100644 index 00000000..ea7c026f --- /dev/null +++ b/pkg/server/views/layouts/navbar.gohtml @@ -0,0 +1,68 @@ +{{define "navbar"}} +
+
+
+ + +
+ {{if .User}} + + {{end}} +
+
+
+ +
+{{end}} + +{{define "accountDropdown"}} + +{{end}} + +{{define "logoutForm"}} +
+ {{csrfField}} + +
+{{end}} diff --git a/pkg/server/views/notes/index.gohtml b/pkg/server/views/notes/index.gohtml new file mode 100644 index 00000000..168430f5 --- /dev/null +++ b/pkg/server/views/notes/index.gohtml @@ -0,0 +1,91 @@ +{{define "yield"}} +
+

Notes

+ + {{template "pageToolbar" dict "data" . "class" "toolbar"}} + +
+ {{if eq (len .NoteGroups) 0 }} +
No notes found.
+ {{end}} + + {{range .NoteGroups}} + {{template "noteGroup" .}} + {{end}} +
+
+{{end}} + +{{define "noteGroup"}} +
+
+

+ +

+
+ +
    + {{range .Data}} + {{template "noteItem" .}} + {{end}} +
+
+{{end}} + +{{define "noteItem"}} +
  • + +
    +
    +

    + {{ .Book.Label }} +

    + + {{template "time" dict "value" .UpdatedAt "text" (timeAgo .UpdatedAt)}} +
    + +
    + {{ excerpt .Body 160 }} +
    +
    +
    +
  • +{{end}} + +{{define "pageToolbarContent"}} + +{{end}} + +{{define "pager"}} + +{{$ariaLabel := ""}} +{{if eq .direction "left"}} + {{$ariaLabel = "Previous page"}} +{{else}} + {{$ariaLabel = "Next page"}} +{{end}} + +{{if .disabled}} + + {{template "caret" dict "direction" .direction "stroke" "gray"}} + +{{else}} + + {{template "caret" dict "direction" .direction "stroke" "black"}} + +{{end}} +{{end}} diff --git a/pkg/server/views/notes/show.gohtml b/pkg/server/views/notes/show.gohtml new file mode 100644 index 00000000..7051bee4 --- /dev/null +++ b/pkg/server/views/notes/show.gohtml @@ -0,0 +1,33 @@ +{{define "yield"}} +
    +
    +
    +
    +
    + {{template "book" dict "fill" "#000000"}} + +

    + + {{ .Note.Book.Label }} + +

    +
    +
    + + +
    +
    + {{ .Content }} +
    +
    + +
    +
    + Last edit: + {{ timeFormat .Note.UpdatedAt "January 02, 2006" }} +
    +
    +
    +
    +
    +{{end}} diff --git a/pkg/server/views/partials/page_toolbar.gohtml b/pkg/server/views/partials/page_toolbar.gohtml new file mode 100644 index 00000000..4d1abdfd --- /dev/null +++ b/pkg/server/views/partials/page_toolbar.gohtml @@ -0,0 +1,5 @@ +{{define "pageToolbar"}} +
    + {{template "pageToolbarContent" .data}} +
    +{{end}} diff --git a/pkg/server/views/partials/settings_sidebar.gohtml b/pkg/server/views/partials/settings_sidebar.gohtml new file mode 100644 index 00000000..0d3e5edf --- /dev/null +++ b/pkg/server/views/partials/settings_sidebar.gohtml @@ -0,0 +1,23 @@ +{{define "settingsSidebar"}} + +{{end}} diff --git a/pkg/server/views/partials/time.gohtml b/pkg/server/views/partials/time.gohtml new file mode 100644 index 00000000..b05b3a86 --- /dev/null +++ b/pkg/server/views/partials/time.gohtml @@ -0,0 +1,13 @@ +{{define "time"}} + +{{$mobileText := defaultValue .mobileText .text}} + + + + +{{end}} diff --git a/pkg/server/views/static/not_found.gohtml b/pkg/server/views/static/not_found.gohtml new file mode 100644 index 00000000..baef1f4a --- /dev/null +++ b/pkg/server/views/static/not_found.gohtml @@ -0,0 +1,3 @@ +{{define "yield"}} +

    Page not found

    +{{end}} diff --git a/pkg/server/views/time.go b/pkg/server/views/time.go new file mode 100644 index 00000000..dad46c46 --- /dev/null +++ b/pkg/server/views/time.go @@ -0,0 +1,103 @@ +package views + +import ( + "fmt" + "time" +) + +type timeDiff struct { + text string + tense string +} + +func pluralize(singular string, count int) string { + var noun string + if count == 1 { + noun = singular + } else { + noun = singular + "s" + } + + return noun +} + +func abs(num int64) int64 { + if num < 0 { + return -num + } + + return num +} + +var ( + DAY = 24 * time.Hour.Milliseconds() + WEEK = 7 * DAY +) + +func getTimeDiffText(interval int64, noun string) string { + return fmt.Sprintf("%d %s", interval, pluralize(noun, int(interval))) +} + +func relativeTimeDiff(t1, t2 time.Time) timeDiff { + diff := t1.Sub(t2) + ts := abs(diff.Milliseconds()) + + var tense string + if diff > 0 { + tense = "past" + } else { + tense = "future" + } + + interval := ts / (52 * WEEK) + if interval >= 1 { + return timeDiff{ + text: getTimeDiffText(interval, "year"), + tense: tense, + } + } + + interval = ts / (4 * WEEK) + if interval >= 1 { + return timeDiff{ + text: getTimeDiffText(interval, "month"), + tense: tense, + } + } + + interval = ts / WEEK + if interval >= 1 { + return timeDiff{ + text: getTimeDiffText(interval, "week"), + tense: tense, + } + } + + interval = ts / DAY + if interval >= 1 { + return timeDiff{ + text: getTimeDiffText(interval, "day"), + tense: tense, + } + } + + interval = ts / time.Hour.Milliseconds() + if interval >= 1 { + return timeDiff{ + text: getTimeDiffText(interval, "hour"), + tense: tense, + } + } + + interval = ts / time.Minute.Milliseconds() + if interval >= 1 { + return timeDiff{ + text: getTimeDiffText(interval, "minute"), + tense: tense, + } + } + + return timeDiff{ + text: "Just now", + } +} diff --git a/pkg/server/views/users/email_verification.gohtml b/pkg/server/views/users/email_verification.gohtml new file mode 100644 index 00000000..969688a4 --- /dev/null +++ b/pkg/server/views/users/email_verification.gohtml @@ -0,0 +1,2 @@ +{{define "yield"}} +{{end}} diff --git a/pkg/server/views/users/login.gohtml b/pkg/server/views/users/login.gohtml new file mode 100644 index 00000000..1ee81737 --- /dev/null +++ b/pkg/server/views/users/login.gohtml @@ -0,0 +1,76 @@ +{{define "yield"}} +
    +
    + + {{template "logo" .}} + + +

    Sign in to Dnote

    + +
    + {{if .Referrer}} + + {{end}} + +
    + {{if .Alert}} + + {{end}} + + {{template "loginForm" .}} +
    +
    + + +
    +
    +{{end}} + +{{define "loginForm"}} +
    + {{csrfField}} + +
    + +
    + +
    + +
    + + +
    +{{end}} diff --git a/pkg/server/views/users/new.gohtml b/pkg/server/views/users/new.gohtml new file mode 100644 index 00000000..e2d703bb --- /dev/null +++ b/pkg/server/views/users/new.gohtml @@ -0,0 +1,86 @@ +{{define "yield"}} +
    +
    + + {{template "logo" .}} + + +

    Join Dnote

    + +
    + {{if .Referrer}} + + {{end}} + +
    + {{if .Alert}} + + {{end}} + + {{template "signupForm" .}} +
    +
    + + +
    +
    +{{end}} + +{{define "signupForm"}} +
    + {{csrfField}} + +
    +
    + +
    + +
    + +
    + +
    + +
    + + +
    +
    +{{end}} diff --git a/pkg/server/views/users/password_reset.gohtml b/pkg/server/views/users/password_reset.gohtml new file mode 100644 index 00000000..6d7201d5 --- /dev/null +++ b/pkg/server/views/users/password_reset.gohtml @@ -0,0 +1,52 @@ +{{define "yield"}} +
    +
    + + {{template "logo" .}} + +

    Reset Password

    + +
    +
    + {{if .Alert}} + + {{end}} + + {{template "passwordResetForm" .}} +
    + + +
    +
    +
    +{{end}} + +{{define "passwordResetForm"}} +
    + {{csrfField}} + +
    + +
    + + +
    +{{end}} diff --git a/pkg/server/views/users/password_reset_confirm.gohtml b/pkg/server/views/users/password_reset_confirm.gohtml new file mode 100644 index 00000000..ddbb2ac7 --- /dev/null +++ b/pkg/server/views/users/password_reset_confirm.gohtml @@ -0,0 +1,66 @@ +{{define "yield"}} +
    +
    + + {{template "logo" .}} + +

    Reset Password

    + + +
    + + +
    + {{if .Alert}} + + {{end}} + + {{template "passwordResetConfirmForm" .}} +
    +
    +
    +
    +{{end}} + +{{define "passwordResetConfirmForm"}} +
    + {{csrfField}} + + + + +
    + +
    + +
    + +
    + + +
    +{{end}} diff --git a/pkg/server/views/users/settings.gohtml b/pkg/server/views/users/settings.gohtml new file mode 100644 index 00000000..a2af6649 --- /dev/null +++ b/pkg/server/views/users/settings.gohtml @@ -0,0 +1,241 @@ +{{define "yield"}} +
    +
    + + +
    +
    + {{template "settingsSidebar" .}} +
    + +
    +
    + {{if ne .Standalone "true"}} + {{template "planSection" .}} + {{end}} + {{template "emailSection" .}} + {{template "passwordSection" .}} +
    +
    +
    +
    +
    +{{end}} + +{{define "emailForm"}} +
    + {{/* prevent browsers from automatically filling the input fields */}} + + + +
    + + + +
    + +
    + + + +
    + +
    + +
    +
    +{{end}} + +{{define "passwordChangeForm"}} +
    + {{/* prevent browsers from automatically filling the input fields */}} + + + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + +
    +
    +{{end}} + +{{define "emailSection"}} +
    +

    Email

    + +
    +
    +
    +

    Current Email

    +
    + +
    + {{.Email}} +
    +
    +
    + +
    +
    +
    +

    Email Verified

    +
    + +
    + {{ if eq true false }} b{{end}} + + {{if .EmailVerified}} + Yes + {{else}} + No + + + {{end}} +
    +
    +
    + +
    +
    +
    +

    Change Email

    +
    +
    + +
    + {{template "emailForm" .}} +
    +
    +
    +{{end}} + +{{define "passwordSection"}} +
    +

    Password

    + +
    +
    +
    +

    Change Password

    +

    + Set a unique password to protect your data. +

    +
    +
    + +
    + {{template "passwordChangeForm" .}} +
    +
    +
    +{{end}} + +{{define "planSection"}} +
    +

    Plan

    + +
    +
    +
    +

    Dnote Pro

    +

    + Fully hosted and managed Dnote for you. +

    +
    + +
    + {{if .Cloud}} + Yes + {{else}} + + Unlock + + {{end}} +
    +
    + +
    +
    +{{end}} diff --git a/pkg/server/views/users/settings_about.gohtml b/pkg/server/views/users/settings_about.gohtml new file mode 100644 index 00000000..bba9a8f0 --- /dev/null +++ b/pkg/server/views/users/settings_about.gohtml @@ -0,0 +1,57 @@ +{{define "yield"}} +
    +
    + + +
    +
    + {{template "settingsSidebar" .}} +
    + +
    +
    +
    +

    Software

    + +
    +
    +
    +

    Version

    +
    + +
    + {{.Version}} +
    +
    +
    + + {{if ne .Standalone "true"}} +
    +
    +
    +

    Support

    +
    + +
    + {{if .User.Cloud}} + + support@getdnote.com + + {{else}} + Not eligible + {{end}} +
    +
    +
    + {{else}} + + {{end}} +
    +
    +
    +
    +
    +
    +{{end}} diff --git a/pkg/server/views/view.go b/pkg/server/views/view.go new file mode 100644 index 00000000..db139c3b --- /dev/null +++ b/pkg/server/views/view.go @@ -0,0 +1,203 @@ +package views + +import ( + "bytes" + "fmt" + "html/template" + "io" + "net/http" + "path/filepath" + + "github.com/dnote/dnote/pkg/clock" + "github.com/dnote/dnote/pkg/server/app" + "github.com/dnote/dnote/pkg/server/buildinfo" + "github.com/dnote/dnote/pkg/server/context" + "github.com/dnote/dnote/pkg/server/log" + "github.com/gorilla/csrf" + "github.com/pkg/errors" +) + +const ( + // templateExt is the template extension + templateExt string = ".gohtml" +) + +const ( + siteTitle = "Dnote" +) + +const ( + ServerErrorPageFileKey = "500" +) + +// Config is a view config +type Config struct { + Title string + Layout string + HeaderTemplate string + HelperFuncs map[string]interface{} + AlertInBody bool + Clock clock.Clock +} + +type viewCtx struct { + Clock clock.Clock + Config Config +} + +func newViewCtx(c Config) viewCtx { + return viewCtx{ + Clock: c.getClock(), + Config: c, + } +} + +func (c Config) getLayout() string { + if c.Layout == "" { + return "base" + } + + return c.Layout +} + +func (c Config) getClock() clock.Clock { + if c.Clock != nil { + return c.Clock + } + + return clock.New() +} + +// NewView returns a new view by parsing the given layout and files +func NewView(baseDir string, app *app.App, viewConfig Config, files ...string) *View { + addTemplatePath(baseDir, files) + addTemplateExt(files) + + files = append(files, iconFiles(baseDir)...) + files = append(files, layoutFiles(baseDir)...) + files = append(files, partialFiles(baseDir)...) + + viewHelpers := initHelpers(viewConfig, app) + t := template.New(viewConfig.Title).Funcs(viewHelpers) + + t, err := t.ParseFiles(files...) + if err != nil { + panic(errors.Wrap(err, "instantiating view")) + } + + return &View{ + Template: t, + Layout: viewConfig.getLayout(), + AlertInBody: viewConfig.AlertInBody, + Files: app.Files, + } +} + +// View holds the information about a view +type View struct { + Template *template.Template + Layout string + // AlertInBody specifies if alert should be set in the body instead of the header + AlertInBody bool + Files map[string][]byte +} + +func (v *View) ServeHTTP(w http.ResponseWriter, r *http.Request) { + v.Render(w, r, nil, http.StatusOK) +} + +// Render is used to render the view with the predefined layout +func (v *View) Render(w http.ResponseWriter, r *http.Request, data *Data, statusCode int) { + w.Header().Set("Content-Type", "text/html") + + var vd Data + if data != nil { + vd = *data + } + + if alert := getAlert(r); alert != nil { + vd.PutAlert(*alert, v.AlertInBody) + clearAlert(w) + } + + vd.User = context.User(r.Context()) + vd.Account = context.Account(r.Context()) + + // Put user data in Yield + if vd.Yield == nil { + vd.Yield = map[string]interface{}{} + } + if vd.Account != nil { + vd.Yield["Email"] = vd.Account.Email.String + vd.Yield["EmailVerified"] = vd.Account.EmailVerified + vd.Yield["EmailVerified"] = vd.Account.EmailVerified + } + if vd.User != nil { + vd.Yield["Cloud"] = vd.User.Cloud + } + vd.Yield["CurrentPath"] = r.URL.Path + vd.Yield["Standalone"] = buildinfo.Standalone + + var buf bytes.Buffer + csrfField := csrf.TemplateField(r) + tpl := v.Template.Funcs(template.FuncMap{ + "csrfField": func() template.HTML { + return csrfField + }, + }) + + if err := tpl.ExecuteTemplate(&buf, v.Layout, vd); err != nil { + log.ErrorWrap(err, fmt.Sprintf("executing template for URI '%s'", r.RequestURI)) + w.WriteHeader(http.StatusInternalServerError) + w.Write(v.Files[ServerErrorPageFileKey]) + return + } + + w.WriteHeader(statusCode) + io.Copy(w, &buf) +} + +func getFiles(pattern string) []string { + files, err := filepath.Glob(pattern) + if err != nil { + panic(err) + } + + return files +} + +// layoutFiles returns a slice of strings representing +// the layout files used in our application. +func layoutFiles(baseDir string) []string { + return getFiles(fmt.Sprintf("%s/layouts/*%s", baseDir, templateExt)) +} + +// iconFiles returns a slice of strings representing +// the icon files used in our application. +func iconFiles(baseDir string) []string { + return getFiles(fmt.Sprintf("%s/icons/*%s", baseDir, templateExt)) +} + +func partialFiles(baseDir string) []string { + return getFiles(fmt.Sprintf("%s/partials/*%s", baseDir, templateExt)) +} + +// addTemplatePath takes in a slice of strings +// representing file paths for templates. +func addTemplatePath(baseDir string, files []string) { + for i, f := range files { + files[i] = fmt.Sprintf("%s/%s", baseDir, f) + } +} + +// addTemplateExt takes in a slice of strings +// representing file paths for templates and it appends +// the templateExt extension to each string in the slice +// +// Eg the input {"home"} would result in the output +// {"home.gohtml"} if templateExt == ".gohtml" +func addTemplateExt(files []string) { + for i, f := range files { + files[i] = f + templateExt + } +} diff --git a/pkg/server/web/handlers.go b/pkg/server/web/handlers.go index 854dbbb7..3eeeb6dc 100644 --- a/pkg/server/web/handlers.go +++ b/pkg/server/web/handlers.go @@ -22,7 +22,7 @@ package web import ( "net/http" - "github.com/dnote/dnote/pkg/server/handlers" + "github.com/dnote/dnote/pkg/server/middleware" "github.com/dnote/dnote/pkg/server/tmpl" "github.com/jinzhu/gorm" "github.com/pkg/errors" @@ -106,9 +106,9 @@ func getRootHandler(c Context) http.HandlerFunc { buf, err := appShell.Execute(r) if err != nil { if errors.Cause(err) == tmpl.ErrNotFound { - handlers.RespondNotFound(w) + middleware.RespondNotFound(w) } else { - handlers.DoError(w, "executing app shell", err, http.StatusInternalServerError) + middleware.DoError(w, "executing app shell", err, http.StatusInternalServerError) } return } diff --git a/pkg/watcher/main.go b/pkg/watcher/main.go index 041fb774..4b2b4720 100644 --- a/pkg/watcher/main.go +++ b/pkg/watcher/main.go @@ -19,11 +19,13 @@ package main import ( + "encoding/csv" "flag" "log" "os" "os/exec" "os/signal" + "regexp" "strings" "syscall" "time" @@ -32,6 +34,23 @@ import ( "github.com/radovskyb/watcher" ) +// splitCommandParts splits the given commad string at space, except +// when inside a double quotation mark. +func splitCommandParts(cmd string) []string { + re := regexp.MustCompile(`\r?\n`) + s := re.ReplaceAllString(cmd, " ") + + r := csv.NewReader(strings.NewReader(s)) + r.Comma = ' ' + + fields, err := r.Read() + if err != nil { + panic(err) + } + + return fields +} + func command(binary string, args []string, entryPoint string) *exec.Cmd { cmd := exec.Command(binary, args...) @@ -52,7 +71,7 @@ func command(binary string, args []string, entryPoint string) *exec.Cmd { } func execCmd(task string, watchDir string) *exec.Cmd { - parts := strings.Fields(task) + parts := splitCommandParts(task) return command(parts[0], parts[1:], watchDir) } diff --git a/scripts/server/test-local.sh b/scripts/server/test-local.sh index 9f99e08a..ce50d7d6 100755 --- a/scripts/server/test-local.sh +++ b/scripts/server/test-local.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # shellcheck disable=SC1090 # test-local.sh runs api tests using local setting -set -eux +set -ex dir=$(dirname "${BASH_SOURCE[0]}") @@ -9,4 +9,4 @@ set -a source "$dir/../../pkg/server/.env.test" set +a -"$dir/test.sh" +"$dir/test.sh" "$1" diff --git a/scripts/server/test.sh b/scripts/server/test.sh index 89d51a79..a2d9ac02 100755 --- a/scripts/server/test.sh +++ b/scripts/server/test.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # test.sh runs server tests. It is to be invoked by other scripts that set # appropriate env vars. -set -eux +set -ex dir=$(realpath "$(dirname "${BASH_SOURCE[0]}")") pushd "$dir/../../pkg/server" @@ -10,7 +10,11 @@ emailTemplateDir=$(realpath "$dir/../../pkg/server/mailer/templates/src") export DNOTE_TEST_EMAIL_TEMPLATE_DIR="$emailTemplateDir" function run_test { - go test ./... -cover -p 1 + if [ -z "$1" ]; then + go test ./... -cover -p 1 + else + go test -run "$1" -cover -p 1 + fi } if [ "${WATCH-false}" == true ]; then @@ -18,7 +22,7 @@ if [ "${WATCH-false}" == true ]; then while inotifywait --exclude .swp -e modify -r .; do run_test; done; set -e else - run_test + run_test "$1" fi popd diff --git a/scripts/vagrant/install_utils.sh b/scripts/vagrant/install_utils.sh index 1c06d413..46322005 100755 --- a/scripts/vagrant/install_utils.sh +++ b/scripts/vagrant/install_utils.sh @@ -9,3 +9,10 @@ wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-ke echo 'deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main' | sudo tee /etc/apt/sources.list.d/google-chrome.list sudo apt-get -y update sudo apt-get install -y google-chrome-stable + +# Install dart-sass +dart_version=1.34.1 +dart_tarball="dart-sass-$dart_version-linux-x64.tar.gz" +wget -q "https://github.com/sass/dart-sass/releases/download/$dart_version/$dart_tarball" +tar -xvzf "$dart_tarball" -C /tmp/ +sudo install /tmp/dart-sass/sass /usr/bin diff --git a/scripts/web/dev.sh b/scripts/web/dev.sh index 96cfc6f7..11f1e207 100755 --- a/scripts/web/dev.sh +++ b/scripts/web/dev.sh @@ -3,17 +3,9 @@ # dev.sh builds and starts development environment set -eux -o pipefail -# clean up background processes -function cleanup { - kill "$devServerPID" -} -trap cleanup EXIT - dir=$(dirname "${BASH_SOURCE[0]}") basePath="$dir/../.." -appPath="$basePath/web" serverPath="$basePath/pkg/server" -serverPort=3000 # load env set -a @@ -21,20 +13,22 @@ dotenvPath="$serverPath/.env.dev" source "$dotenvPath" set +a -# run webpack-dev-server for js in the background -( - BUNDLE_BASE_URL=http://localhost:8080 \ - ASSET_BASE_URL=http://localhost:3000/static \ - ROOT_URL=http://localhost:$serverPort \ - COMPILED_PATH="$appPath"/compiled \ - PUBLIC_PATH="$appPath"/public \ - COMPILED_PATH="$basePath/web/compiled" \ - STANDALONE=true \ - VERSION="$VERSION" \ - WEBPACK_HOST="0.0.0.0" \ - "$dir/webpack-dev.sh" -) & -devServerPID=$! +# copy assets +mkdir -p "$basePath/pkg/server/static" +cp "$basePath"/pkg/server/assets/static/* "$basePath/pkg/server/static" +# run asset pipeline in the background +(cd "$basePath/pkg/server/assets/" && "$basePath/pkg/server/assets/styles/build.sh" true ) & +(cd "$basePath/pkg/server/assets/" && "$basePath/pkg/server/assets/js/build.sh" true ) & # run server -(cd "$basePath/pkg/watcher" && go run main.go --task="go run main.go start -port 3000" --context="$serverPath" "$serverPath") +moduleName="github.com/dnote/dnote" +ldflags="-X '$moduleName/pkg/server/buildinfo.CSSFiles=main.css' -X '$moduleName/pkg/server/buildinfo.JSFiles=main.js' -X '$moduleName/pkg/server/buildinfo.Version=dev' -X '$moduleName/pkg/server/buildinfo.Standalone=true'" +task="go run -ldflags \"$ldflags\" main.go start -port 3000" + +( + cd "$basePath/pkg/watcher" && \ + go run main.go \ + --task="$task" \ + --context="$serverPath" \ + "$serverPath" +) diff --git a/test/cli/test-cli b/test/cli/test-cli new file mode 100755 index 0000000000000000000000000000000000000000..a4665feb1b360b5ca6281879c4c1a8fa5665eaba GIT binary patch literal 16934752 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%0D)FG+w$=>& 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ⅅ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�ixNJ{K+EKdW=)i)L|sP8o_{R@_JB|HC- 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 l)%8}i9YAboTAwi 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


    *+TYhk~}^9f4R925Z31pP?l>JPKrW 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