From 8cf7e4147f3f676da15685456a53228e4db3c80b Mon Sep 17 00:00:00 2001 From: orosmatthew Date: Sat, 26 Aug 2023 10:36:57 -0400 Subject: [PATCH 01/10] Update web deps --- web/package-lock.json | 32 ++++++++++++++++---------------- web/package.json | 6 +++--- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/web/package-lock.json b/web/package-lock.json index ea2e76c..653aaa2 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -30,18 +30,18 @@ "@sveltejs/kit": "^1.23.0", "@types/bootstrap": "^5.2.6", "@types/diff": "^5.0.3", - "@types/node": "^20.5.4", + "@types/node": "^20.5.6", "@types/uuid": "^9.0.2", "@typescript-eslint/eslint-plugin": "^6.4.1", "@typescript-eslint/parser": "^6.4.1", - "eslint": "^8.47.0", + "eslint": "^8.48.0", "eslint-config-prettier": "^9.0.0", "prettier": "^3.0.2", "prettier-plugin-svelte": "^3.0.3", "svelte": "^4.2.0", "svelte-check": "^3.5.0", "tslib": "^2.6.2", - "typescript": "^5.1.6", + "typescript": "^5.2.2", "vite": "^4.4.9" } }, @@ -440,9 +440,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.47.0.tgz", - "integrity": "sha512-P6omY1zv5MItm93kLM8s2vr1HICJH8v0dvddDhysbIuZ+vcjOHg5Zbkf1mTkcmi2JA9oBG2anOkRnW8WJTS8Og==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.48.0.tgz", + "integrity": "sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } @@ -893,9 +893,9 @@ } }, "node_modules/@types/node": { - "version": "20.5.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.4.tgz", - "integrity": "sha512-Y9vbIAoM31djQZrPYjpTLo0XlaSwOIsrlfE3LpulZeRblttsLQRFRlBAppW0LOxyT3ALj2M5vU1ucQQayQH3jA==" + "version": "20.5.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.6.tgz", + "integrity": "sha512-Gi5wRGPbbyOTX+4Y2iULQ27oUPrefaB0PxGQJnfyWN3kvEDGM3mIB5M/gQLmitZf7A9FmLeaqxD3L1CXpm3VKQ==" }, "node_modules/@types/pug": { "version": "2.0.6", @@ -1639,14 +1639,14 @@ } }, "node_modules/eslint": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.47.0.tgz", - "integrity": "sha512-spUQWrdPt+pRVP1TTJLmfRNJJHHZryFmptzcafwSvHsceV81djHOdnEeDmkdotZyLNjDhrOasNK8nikkoG1O8Q==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.48.0.tgz", + "integrity": "sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg==", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "^8.47.0", + "@eslint/js": "8.48.0", "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -3524,9 +3524,9 @@ } }, "node_modules/typescript": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", - "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", "dev": true, "bin": { "tsc": "bin/tsc", diff --git a/web/package.json b/web/package.json index 81333fb..7238a9d 100644 --- a/web/package.json +++ b/web/package.json @@ -16,18 +16,18 @@ "@sveltejs/kit": "^1.23.0", "@types/bootstrap": "^5.2.6", "@types/diff": "^5.0.3", - "@types/node": "^20.5.4", + "@types/node": "^20.5.6", "@types/uuid": "^9.0.2", "@typescript-eslint/eslint-plugin": "^6.4.1", "@typescript-eslint/parser": "^6.4.1", - "eslint": "^8.47.0", + "eslint": "^8.48.0", "eslint-config-prettier": "^9.0.0", "prettier": "^3.0.2", "prettier-plugin-svelte": "^3.0.3", "svelte": "^4.2.0", "svelte-check": "^3.5.0", "tslib": "^2.6.2", - "typescript": "^5.1.6", + "typescript": "^5.2.2", "vite": "^4.4.9" }, "type": "module", From fe46b0eac28fbf11837b6f6210fe88f9cf404127 Mon Sep 17 00:00:00 2001 From: orosmatthew Date: Sat, 26 Aug 2023 10:48:43 -0400 Subject: [PATCH 02/10] [web] Remove unneeded deps --- web/package-lock.json | 210 +----------------------------------------- web/package.json | 5 +- 2 files changed, 4 insertions(+), 211 deletions(-) diff --git a/web/package-lock.json b/web/package-lock.json index 653aaa2..8d8c205 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -11,14 +11,11 @@ "@prisma/client": "^5.2.0", "@sveltejs/adapter-node": "^1.3.1", "@types/fs-extra": "^11.0.1", - "axios": "^1.4.0", "bootstrap": "^5.3.1", "diff": "^5.1.0", "diff2html": "^3.4.40", "eslint-plugin-svelte": "^2.33.0", "fs-extra": "^11.1.1", - "highlight.js": "^11.8.0", - "memfs": "^4.2.1", "node-git-server": "^1.0.0", "prisma": "^5.2.0", "simple-git": "^3.19.1", @@ -1183,11 +1180,6 @@ "node": ">= 8" } }, - "node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" - }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -1210,21 +1202,6 @@ "node": ">=8" } }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "node_modules/axios": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", - "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", - "dependencies": { - "follow-redirects": "^1.15.0", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, "node_modules/axobject-query": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", @@ -1415,17 +1392,6 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", @@ -1509,14 +1475,6 @@ "node": ">=0.10.0" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -1831,12 +1789,6 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, - "node_modules/fast-diff": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", - "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", - "peer": true - }, "node_modules/fast-glob": { "version": "3.2.12", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", @@ -1938,38 +1890,6 @@ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==" }, - "node_modules/follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/fs-extra": { "version": "11.1.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", @@ -2103,6 +2023,7 @@ "version": "11.8.0", "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.8.0.tgz", "integrity": "sha512-MedQhoqVdr0U6SSnWPzfiadUcDHfN/Wzq25AkXiQv9oiOO/sG0S7XkvpFIqWBl9Yq1UYyYOOVORs5UW2XlPyzg==", + "optional": true, "engines": { "node": ">=12.0.0" } @@ -2128,14 +2049,6 @@ "node": "*" } }, - "node_modules/hyperdyperid": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz", - "integrity": "sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==", - "engines": { - "node": ">=10.18" - } - }, "node_modules/ignore": { "version": "5.2.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", @@ -2293,35 +2206,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/json-joy": { - "version": "9.5.1", - "resolved": "https://registry.npmjs.org/json-joy/-/json-joy-9.5.1.tgz", - "integrity": "sha512-XMSpdxaiWUZlc+CAUbPS3G2MZbGxm6clFatqjta/DLrq5V4Y5JU4cx7Qvy7l+XTVPvmRWaYuzzAuCf9uUc40IA==", - "dependencies": { - "arg": "^5.0.2", - "hyperdyperid": "^1.2.0" - }, - "bin": { - "json-pack": "bin/json-pack.js", - "json-pack-test": "bin/json-pack-test.js", - "json-patch": "bin/json-patch.js", - "json-patch-test": "bin/json-patch-test.js", - "json-pointer": "bin/json-pointer.js", - "json-pointer-test": "bin/json-pointer-test.js", - "json-unpack": "bin/json-unpack.js" - }, - "engines": { - "node": ">=10.0" - }, - "funding": { - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "quill-delta": "^5", - "rxjs": "7", - "tslib": "2" - } - }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -2395,18 +2279,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", - "peer": true - }, - "node_modules/lodash.isequal": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", - "peer": true - }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -2439,25 +2311,6 @@ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==" }, - "node_modules/memfs": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.2.1.tgz", - "integrity": "sha512-CINEB6cNAAhLUfRGrB4lj2Pj47ygerEmw3jxPb6R1gkD6Jfp484gJLteQ6MzqIjGWtFWuVzDl+KN7HiipMuKSw==", - "dependencies": { - "json-joy": "^9.2.0", - "thingies": "^1.11.1" - }, - "engines": { - "node": ">= 4.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" - } - }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -2491,25 +2344,6 @@ "node": ">=10.0.0" } }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -2924,11 +2758,6 @@ "node": ">=16.13" } }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" - }, "node_modules/punycode": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", @@ -2956,20 +2785,6 @@ } ] }, - "node_modules/quill-delta": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-5.1.0.tgz", - "integrity": "sha512-X74oCeRI4/p0ucjb5Ma8adTXd9Scumz367kkMK5V/IatcX6A0vlgLgKbzXWy5nZmCGeNJm2oQX0d2Eqj+ZIlCA==", - "peer": true, - "dependencies": { - "fast-diff": "^1.3.0", - "lodash.clonedeep": "^4.5.0", - "lodash.isequal": "^4.5.0" - }, - "engines": { - "node": ">= 12.0.0" - } - }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -3066,15 +2881,6 @@ "queue-microtask": "^1.2.2" } }, - "node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "peer": true, - "dependencies": { - "tslib": "^2.1.0" - } - }, "node_modules/sade": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", @@ -3448,17 +3254,6 @@ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" }, - "node_modules/thingies": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/thingies/-/thingies-1.12.0.tgz", - "integrity": "sha512-AiGqfYC1jLmJagbzQGuoZRM48JPsr9yB734a7K6wzr34NMhjUPrWSQrkF7ZBybf3yCerCL2Gcr02kMv4NmaZfA==", - "engines": { - "node": ">=10.18" - }, - "peerDependencies": { - "tslib": "^2" - } - }, "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -3499,7 +3294,8 @@ "node_modules/tslib": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true }, "node_modules/type-check": { "version": "0.4.0", diff --git a/web/package.json b/web/package.json index 7238a9d..fd7edea 100644 --- a/web/package.json +++ b/web/package.json @@ -4,7 +4,7 @@ "private": true, "scripts": { "dev": "vite dev", - "build": "prisma generate && vite build", + "build": "svelte-kit sync && prisma generate && vite build", "preview": "vite preview", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", @@ -35,14 +35,11 @@ "@prisma/client": "^5.2.0", "@sveltejs/adapter-node": "^1.3.1", "@types/fs-extra": "^11.0.1", - "axios": "^1.4.0", "bootstrap": "^5.3.1", "diff": "^5.1.0", "diff2html": "^3.4.40", "eslint-plugin-svelte": "^2.33.0", "fs-extra": "^11.1.1", - "highlight.js": "^11.8.0", - "memfs": "^4.2.1", "node-git-server": "^1.0.0", "prisma": "^5.2.0", "simple-git": "^3.19.1", From efeb27516b62cd488856c136b96aea3fbe6ee57c Mon Sep 17 00:00:00 2001 From: orosmatthew Date: Sat, 26 Aug 2023 11:07:09 -0400 Subject: [PATCH 03/10] [web] Improve admin auth --- web/package-lock.json | 403 ++++++++++++++++++++++++++ web/package.json | 2 + web/prisma/schema.prisma | 11 +- web/src/hooks.server.ts | 69 ++--- web/src/lib/server/auth.ts | 89 ++++++ web/src/routes/login/+page.server.ts | 13 +- web/src/routes/logout/+page.server.ts | 12 +- 7 files changed, 528 insertions(+), 71 deletions(-) create mode 100644 web/src/lib/server/auth.ts diff --git a/web/package-lock.json b/web/package-lock.json index 8d8c205..d189690 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -11,6 +11,7 @@ "@prisma/client": "^5.2.0", "@sveltejs/adapter-node": "^1.3.1", "@types/fs-extra": "^11.0.1", + "bcrypt": "^5.1.1", "bootstrap": "^5.3.1", "diff": "^5.1.0", "diff2html": "^3.4.40", @@ -25,6 +26,7 @@ "devDependencies": { "@sveltejs/adapter-auto": "^2.1.0", "@sveltejs/kit": "^1.23.0", + "@types/bcrypt": "^5.0.0", "@types/bootstrap": "^5.2.6", "@types/diff": "^5.0.3", "@types/node": "^20.5.6", @@ -535,6 +537,39 @@ "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==" }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", + "dependencies": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -841,6 +876,15 @@ "vite": "^4.0.0" } }, + "node_modules/@types/bcrypt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.0.tgz", + "integrity": "sha512-agtcFKaruL8TmcvqbndlqHPSJgsolhf/qPWchFlgnW1gECTN/nKbFcoFnvKAQRFfKbh+BO6A3SWdJu9t+xF3Lw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/bootstrap": { "version": "5.2.6", "resolved": "https://registry.npmjs.org/@types/bootstrap/-/bootstrap-5.2.6.tgz", @@ -1130,6 +1174,17 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -1180,6 +1235,23 @@ "node": ">= 8" } }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" + }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -1215,6 +1287,19 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/bcrypt": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", + "integrity": "sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==", + "hasInstallScript": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.11", + "node-addon-api": "^5.0.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -1356,6 +1441,14 @@ "node": ">= 6" } }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "engines": { + "node": ">=10" + } + }, "node_modules/code-red": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz", @@ -1392,6 +1485,14 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "bin": { + "color-support": "bin.js" + } + }, "node_modules/commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", @@ -1402,6 +1503,11 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" + }, "node_modules/cookie": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", @@ -1475,6 +1581,11 @@ "node": ">=0.10.0" } }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" + }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -1492,6 +1603,14 @@ "node": ">=8" } }, + "node_modules/detect-libc": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", + "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", + "engines": { + "node": ">=8" + } + }, "node_modules/devalue": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/devalue/-/devalue-4.3.2.tgz", @@ -1543,6 +1662,11 @@ "node": ">=6.0.0" } }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, "node_modules/es6-promise": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", @@ -1903,6 +2027,28 @@ "node": ">=14.14" } }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -1926,6 +2072,25 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -2019,6 +2184,11 @@ "node": ">=8" } }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" + }, "node_modules/highlight.js": { "version": "11.8.0", "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.8.0.tgz", @@ -2049,6 +2219,18 @@ "node": "*" } }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/ignore": { "version": "5.2.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", @@ -2149,6 +2331,14 @@ "node": ">=0.10.0" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -2306,6 +2496,28 @@ "node": ">=12" } }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/mdn-data": { "version": "2.0.30", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", @@ -2373,6 +2585,37 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", @@ -2428,6 +2671,30 @@ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" }, + "node_modules/node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/node-git-server": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/node-git-server/-/node-git-server-1.0.0.tgz", @@ -2460,6 +2727,25 @@ "node": ">=0.10.0" } }, + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -2785,6 +3071,19 @@ } ] }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -2892,6 +3191,25 @@ "node": ">=6" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/sander": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/sander/-/sander-0.5.1.tgz", @@ -2930,6 +3248,11 @@ "node": ">=10" } }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" + }, "node_modules/set-cookie-parser": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz", @@ -2954,6 +3277,11 @@ "node": ">=8" } }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, "node_modules/simple-git": { "version": "3.19.1", "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.19.1.tgz", @@ -3021,6 +3349,27 @@ "node": ">=10.0.0" } }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -3249,6 +3598,33 @@ "@types/estree": "*" } }, + "node_modules/tar": { + "version": "6.1.15", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.15.tgz", + "integrity": "sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -3279,6 +3655,11 @@ "node": ">=6" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, "node_modules/ts-api-utils": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.2.tgz", @@ -3439,6 +3820,20 @@ } } }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -3453,6 +3848,14 @@ "node": ">= 8" } }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/web/package.json b/web/package.json index fd7edea..9e45630 100644 --- a/web/package.json +++ b/web/package.json @@ -14,6 +14,7 @@ "devDependencies": { "@sveltejs/adapter-auto": "^2.1.0", "@sveltejs/kit": "^1.23.0", + "@types/bcrypt": "^5.0.0", "@types/bootstrap": "^5.2.6", "@types/diff": "^5.0.3", "@types/node": "^20.5.6", @@ -35,6 +36,7 @@ "@prisma/client": "^5.2.0", "@sveltejs/adapter-node": "^1.3.1", "@types/fs-extra": "^11.0.1", + "bcrypt": "^5.1.1", "bootstrap": "^5.3.1", "diff": "^5.1.0", "diff2html": "^3.4.40", diff --git a/web/prisma/schema.prisma b/web/prisma/schema.prisma index 88dbb79..c38d31c 100644 --- a/web/prisma/schema.prisma +++ b/web/prisma/schema.prisma @@ -8,14 +8,15 @@ datasource db { } model User { - id Int @id @default(autoincrement()) - username String @unique - password String - sessions Session[] + id Int @id @default(autoincrement()) + username String @unique + passwordHash String + passwordSalt String + sessions Session[] } model Session { - token String @id + token String @id @default(uuid()) createdAt DateTime @default(now()) user User @relation(fields: [userId], references: [id]) userId Int diff --git a/web/src/hooks.server.ts b/web/src/hooks.server.ts index 357dc2c..69d328c 100644 --- a/web/src/hooks.server.ts +++ b/web/src/hooks.server.ts @@ -1,25 +1,28 @@ import { redirect, type Handle } from '@sveltejs/kit'; -import { db } from '$lib/server/prisma'; -import type { Session } from '@prisma/client'; +import { PrismaClient } from '@prisma/client'; import { startGitServer } from '$lib/server/gitserver'; +import { hashPassword, isSessionValid, logout } from '$lib/server/auth'; -startGitServer(); - -const sessionExpireMilliseconds = 1000 * 60 * 60 * 24; // 24 hours - -function isSessionExpired(session: Session): boolean { - return session.createdAt.valueOf() + sessionExpireMilliseconds < new Date().valueOf(); -} - -async function removeExpiredSessions(userId: number) { - const sessions: Session[] = await db.session.findMany({ where: { userId: userId } }); - sessions.forEach(async (session) => { - if (isSessionExpired(session)) { - await db.session.delete({ where: { token: session.token } }); - } +async function createDefaultAccount(db: PrismaClient) { + const count = await db.user.count(); + if (count !== 0) { + return; + } + const password = await hashPassword('bw123'); + await db.user.create({ + data: { username: 'admin', passwordHash: password.hash, passwordSalt: password.salt } }); } +try { + const db = new PrismaClient(); + createDefaultAccount(db); +} catch (error) { + console.log('Initialization in hooks failed (Normal on build)'); +} + +startGitServer(); + export const handle = (async ({ event, resolve }) => { if (event.request.method === 'OPTIONS') { return new Response('ok', { @@ -33,39 +36,13 @@ export const handle = (async ({ event, resolve }) => { } if (event.url.pathname.startsWith('/login')) { - if (event.cookies.get('token')) { - const session = await db.session.findUnique({ where: { token: event.cookies.get('token') } }); - if (session) { - removeExpiredSessions(session.userId); - if (!isSessionExpired(session)) { - throw redirect(302, '/admin'); - } else { - event.cookies.delete('token'); - const res = resolve(event); - return res; - } - } else { - const res = resolve(event); - return res; - } + if ((await isSessionValid(event.cookies)) === true) { + throw redirect(302, '/admin'); } } if (event.url.pathname.startsWith('/admin')) { - if (event.cookies.get('token')) { - const session = await db.session.findUnique({ where: { token: event.cookies.get('token') } }); - if (session) { - removeExpiredSessions(session.userId); - if (!isSessionExpired(session)) { - const res = await resolve(event); - return res; - } else { - event.cookies.delete('token'); - throw redirect(302, '/login'); - } - } else { - throw redirect(302, '/login'); - } - } else { + if ((await isSessionValid(event.cookies)) !== true) { + logout(event.cookies); throw redirect(302, '/login'); } } diff --git a/web/src/lib/server/auth.ts b/web/src/lib/server/auth.ts new file mode 100644 index 0000000..236e83f --- /dev/null +++ b/web/src/lib/server/auth.ts @@ -0,0 +1,89 @@ +import { redirect, type Cookies } from '@sveltejs/kit'; +import { db } from './prisma'; +import bcrypt from 'bcrypt'; + +export const sessionExpireSeconds = 60 * 60 * 24; // A day + +export async function hashPassword( + password: string, + salt?: string +): Promise<{ salt: string; hash: string }> { + if (salt === undefined) { + salt = await bcrypt.genSalt(); + } + const hash = await bcrypt.hash(password, salt); + return { salt: salt, hash: hash }; +} + +export async function deleteExpiredSessions(userId: number) { + const expirationDate = new Date(new Date().valueOf() - 1000 * sessionExpireSeconds); + await db.session.deleteMany({ + where: { + userId: userId, + createdAt: { lt: expirationDate } + } + }); +} + +export async function logout(cookies: Cookies): Promise { + const sessionCookie = cookies.get('session'); + if (sessionCookie === undefined) { + return false; + } + cookies.delete('session'); + try { + await db.session.delete({ where: { token: sessionCookie } }); + } catch { + return false; + } + return true; +} + +export async function attemptLogin( + cookies: Cookies, + username: string, + password: string +): Promise { + if (username === '' || password === '') { + return false; + } + const user = await db.user.findUnique({ where: { username: username.toString() } }); + if (!user) { + return false; + } + deleteExpiredSessions(user.id); + const hash = await hashPassword(password, user.passwordSalt); + if (user.passwordHash === hash.hash.toString()) { + const session = await db.session.create({ data: { userId: user.id } }); + cookies.set('session', session.token, { + secure: process.env.NODE_ENV === 'development' ? false : true, + httpOnly: true, + sameSite: 'strict', + maxAge: sessionExpireSeconds + }); + return true; + } + return false; +} + +export async function isSessionValid(cookies: Cookies): Promise { + const sessionCookie = cookies.get('session'); + if (sessionCookie === undefined) { + return false; + } + const session = await db.session.findUnique({ where: { token: sessionCookie } }); + if (!session) { + return false; + } + if (new Date().valueOf() - session.createdAt.valueOf() > 1000 * sessionExpireSeconds) { + await db.session.delete({ where: { token: session.token } }); + return false; + } + return true; +} + +export async function redirectIfSessionInvalid(url: string, cookies: Cookies): Promise { + if (!(await isSessionValid(cookies))) { + throw redirect(302, url); + } +} diff --git a/web/src/routes/login/+page.server.ts b/web/src/routes/login/+page.server.ts index f2c4170..cddaa12 100644 --- a/web/src/routes/login/+page.server.ts +++ b/web/src/routes/login/+page.server.ts @@ -1,6 +1,5 @@ import type { Actions } from '@sveltejs/kit'; -import { db } from '$lib/server/prisma'; -import * as UUID from 'uuid'; +import { attemptLogin } from '$lib/server/auth'; export const actions = { login: async ({ cookies, request }) => { @@ -10,16 +9,10 @@ export const actions = { if (!username || !password) { return { success: false }; } - const user = await db.user.findUnique({ where: { username: username } }); - if (!user) { + if ((await attemptLogin(cookies, username, password)) !== true) { return { success: false }; - } - if (user.password === password) { - const uuid: string = UUID.v4(); - await db.session.create({ data: { token: uuid, userId: user.id } }); - cookies.set('token', uuid); + } else { return { success: true }; } - return { success: false }; } } satisfies Actions; diff --git a/web/src/routes/logout/+page.server.ts b/web/src/routes/logout/+page.server.ts index 76bfa14..5ace374 100644 --- a/web/src/routes/logout/+page.server.ts +++ b/web/src/routes/logout/+page.server.ts @@ -1,16 +1,8 @@ import { redirect } from '@sveltejs/kit'; import type { PageServerLoad } from './$types'; -import { db } from '$lib/server/prisma'; +import { logout } from '$lib/server/auth'; export const load = (async ({ cookies }) => { - if (!cookies.get('token')) { - throw redirect(302, '/login'); - } - try { - await db.session.delete({ where: { token: cookies.get('token') } }); - } catch { - throw redirect(302, '/login'); - } - cookies.delete('token'); + await logout(cookies); throw redirect(302, '/login'); }) satisfies PageServerLoad; From fcc23d5da90b6cb53f9743a5a68f21065183bf24 Mon Sep 17 00:00:00 2001 From: orosmatthew Date: Sat, 26 Aug 2023 11:22:43 -0400 Subject: [PATCH 04/10] [web] Improve login page --- web/src/routes/admin/+layout.svelte | 16 ++++- web/src/routes/login/+page.server.ts | 15 +++-- web/src/routes/login/+page.svelte | 90 +++++++++++++++++++-------- web/src/routes/logout/+page.server.ts | 8 --- web/src/routes/logout/+page.svelte | 1 - web/src/routes/logout/+server.ts | 8 +++ 6 files changed, 95 insertions(+), 43 deletions(-) delete mode 100644 web/src/routes/logout/+page.server.ts delete mode 100644 web/src/routes/logout/+page.svelte create mode 100644 web/src/routes/logout/+server.ts diff --git a/web/src/routes/admin/+layout.svelte b/web/src/routes/admin/+layout.svelte index 4dec39b..8ab4ebc 100644 --- a/web/src/routes/admin/+layout.svelte +++ b/web/src/routes/admin/+layout.svelte @@ -1,3 +1,17 @@ + +

diff --git a/web/src/routes/login/+page.server.ts b/web/src/routes/login/+page.server.ts index cddaa12..3a6911c 100644 --- a/web/src/routes/login/+page.server.ts +++ b/web/src/routes/login/+page.server.ts @@ -4,13 +4,16 @@ import { attemptLogin } from '$lib/server/auth'; export const actions = { login: async ({ cookies, request }) => { const data = await request.formData(); - const username = data.get('username')?.toString(); - const password = data.get('password')?.toString(); - if (!username || !password) { - return { success: false }; + const formUsername = data.get('username'); + const formPassword = data.get('password'); + if (formUsername === null || formPassword === null) { + return { success: false, message: 'Incomplete form data' }; } - if ((await attemptLogin(cookies, username, password)) !== true) { - return { success: false }; + if ( + (await attemptLogin(cookies, formUsername.toString().trim(), formPassword.toString())) !== + true + ) { + return { success: false, message: 'Invalid login' }; } else { return { success: true }; } diff --git a/web/src/routes/login/+page.svelte b/web/src/routes/login/+page.svelte index 2279780..6aac31f 100644 --- a/web/src/routes/login/+page.svelte +++ b/web/src/routes/login/+page.svelte @@ -1,37 +1,73 @@ -
-
-

Login

-
- - - - - - -
- -
-
-
- {#if form?.success} - - {:else if form && !form.success} - - {/if} + + {/if} +
+
+ + +
+
+ + +
+
+ +
+
+ + diff --git a/web/src/routes/logout/+page.server.ts b/web/src/routes/logout/+page.server.ts deleted file mode 100644 index 5ace374..0000000 --- a/web/src/routes/logout/+page.server.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { redirect } from '@sveltejs/kit'; -import type { PageServerLoad } from './$types'; -import { logout } from '$lib/server/auth'; - -export const load = (async ({ cookies }) => { - await logout(cookies); - throw redirect(302, '/login'); -}) satisfies PageServerLoad; diff --git a/web/src/routes/logout/+page.svelte b/web/src/routes/logout/+page.svelte deleted file mode 100644 index 43d1e22..0000000 --- a/web/src/routes/logout/+page.svelte +++ /dev/null @@ -1 +0,0 @@ -

Logging Out

diff --git a/web/src/routes/logout/+server.ts b/web/src/routes/logout/+server.ts new file mode 100644 index 0000000..83f3b28 --- /dev/null +++ b/web/src/routes/logout/+server.ts @@ -0,0 +1,8 @@ +import { logout } from '$lib/server/auth'; +import { json } from '@sveltejs/kit'; +import type { RequestHandler } from './$types'; + +export const POST = (async ({ cookies }) => { + await logout(cookies); + return json({ success: true }); +}) satisfies RequestHandler; From c1d86146af68750d11c25ac8abc7d9c641d0f484 Mon Sep 17 00:00:00 2001 From: orosmatthew Date: Sat, 26 Aug 2023 11:41:37 -0400 Subject: [PATCH 05/10] Improve navbar and add dark theme --- web/package-lock.json | 32 +++++++++++ web/package.json | 3 ++ web/src/app.d.ts | 4 +- web/src/app.html | 2 +- web/src/hooks.server.ts | 7 ++- web/src/routes/+layout.server.ts | 5 ++ web/src/routes/+layout.svelte | 17 ++++++ web/src/routes/admin/+layout.svelte | 84 ++++++++++++++++++++--------- web/src/routes/stores.ts | 3 ++ 9 files changed, 130 insertions(+), 27 deletions(-) create mode 100644 web/src/routes/+layout.server.ts create mode 100644 web/src/routes/stores.ts diff --git a/web/package-lock.json b/web/package-lock.json index d189690..c487cbc 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -13,10 +13,12 @@ "@types/fs-extra": "^11.0.1", "bcrypt": "^5.1.1", "bootstrap": "^5.3.1", + "bootstrap-icons": "^1.10.5", "diff": "^5.1.0", "diff2html": "^3.4.40", "eslint-plugin-svelte": "^2.33.0", "fs-extra": "^11.1.1", + "js-cookie": "^3.0.5", "node-git-server": "^1.0.0", "prisma": "^5.2.0", "simple-git": "^3.19.1", @@ -29,6 +31,7 @@ "@types/bcrypt": "^5.0.0", "@types/bootstrap": "^5.2.6", "@types/diff": "^5.0.3", + "@types/js-cookie": "^3.0.3", "@types/node": "^20.5.6", "@types/uuid": "^9.0.2", "@typescript-eslint/eslint-plugin": "^6.4.1", @@ -919,6 +922,12 @@ "@types/node": "*" } }, + "node_modules/@types/js-cookie": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-3.0.3.tgz", + "integrity": "sha512-Xe7IImK09HP1sv2M/aI+48a20VX+TdRJucfq4vfRVy6nWN8PYPOEnlMRSgxJAgYQIXJVL8dZ4/ilAM7dWNaOww==", + "dev": true + }, "node_modules/@types/json-schema": { "version": "7.0.12", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", @@ -1327,6 +1336,21 @@ "@popperjs/core": "^2.11.8" } }, + "node_modules/bootstrap-icons": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.10.5.tgz", + "integrity": "sha512-oSX26F37V7QV7NCE53PPEL45d7EGXmBgHG3pDpZvcRaKVzWMqIRL9wcqJUyEha1esFtM3NJzvmxFXDxjJYD0jQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ] + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -2385,6 +2409,14 @@ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "engines": { + "node": ">=14" + } + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", diff --git a/web/package.json b/web/package.json index 9e45630..2b3e5c5 100644 --- a/web/package.json +++ b/web/package.json @@ -17,6 +17,7 @@ "@types/bcrypt": "^5.0.0", "@types/bootstrap": "^5.2.6", "@types/diff": "^5.0.3", + "@types/js-cookie": "^3.0.3", "@types/node": "^20.5.6", "@types/uuid": "^9.0.2", "@typescript-eslint/eslint-plugin": "^6.4.1", @@ -38,10 +39,12 @@ "@types/fs-extra": "^11.0.1", "bcrypt": "^5.1.1", "bootstrap": "^5.3.1", + "bootstrap-icons": "^1.10.5", "diff": "^5.1.0", "diff2html": "^3.4.40", "eslint-plugin-svelte": "^2.33.0", "fs-extra": "^11.1.1", + "js-cookie": "^3.0.5", "node-git-server": "^1.0.0", "prisma": "^5.2.0", "simple-git": "^3.19.1", diff --git a/web/src/app.d.ts b/web/src/app.d.ts index f59b884..5037988 100644 --- a/web/src/app.d.ts +++ b/web/src/app.d.ts @@ -3,7 +3,9 @@ declare global { namespace App { // interface Error {} - // interface Locals {} + interface Locals { + theme: 'light' | 'dark'; + } // interface PageData {} // interface Platform {} } diff --git a/web/src/app.html b/web/src/app.html index 77ec85d..86694a9 100644 --- a/web/src/app.html +++ b/web/src/app.html @@ -1,5 +1,5 @@ - + diff --git a/web/src/hooks.server.ts b/web/src/hooks.server.ts index 69d328c..1e05a9e 100644 --- a/web/src/hooks.server.ts +++ b/web/src/hooks.server.ts @@ -24,6 +24,9 @@ try { startGitServer(); export const handle = (async ({ event, resolve }) => { + const theme = event.cookies.get('theme') as 'light' | 'dark' | undefined; + event.locals.theme = theme ?? 'dark'; + if (event.request.method === 'OPTIONS') { return new Response('ok', { headers: { @@ -46,6 +49,8 @@ export const handle = (async ({ event, resolve }) => { throw redirect(302, '/login'); } } - const res = await resolve(event); + const res = await resolve(event, { + transformPageChunk: ({ html }) => html.replace('%theme%', event.locals.theme) + }); return res; }) satisfies Handle; diff --git a/web/src/routes/+layout.server.ts b/web/src/routes/+layout.server.ts new file mode 100644 index 0000000..98c7b77 --- /dev/null +++ b/web/src/routes/+layout.server.ts @@ -0,0 +1,5 @@ +import type { LayoutServerLoad } from './$types'; + +export const load = (async ({ locals }) => { + return { theme: locals.theme }; +}) satisfies LayoutServerLoad; diff --git a/web/src/routes/+layout.svelte b/web/src/routes/+layout.svelte index c1d879e..b9c6d09 100644 --- a/web/src/routes/+layout.svelte +++ b/web/src/routes/+layout.svelte @@ -2,9 +2,26 @@ import 'bootstrap/dist/css/bootstrap.min.css'; import '$lib/styles/global.css'; import { onMount } from 'svelte'; + import type { LayoutData } from './$types'; + import { theme } from './stores'; + import { browser } from '$app/environment'; + import Cookies from 'js-cookie'; + import 'bootstrap-icons/font/bootstrap-icons.min.css'; + onMount(async () => { await import('bootstrap'); }); + + export let data: LayoutData; + + $theme = data.theme; + + if (browser) { + theme.subscribe((value) => { + document.getElementById('html-element')?.setAttribute('data-bs-theme', value); + Cookies.set('theme', value, { sameSite: 'strict' }); + }); + } diff --git a/web/src/routes/admin/+layout.svelte b/web/src/routes/admin/+layout.svelte index 8ab4ebc..bf6d7ee 100644 --- a/web/src/routes/admin/+layout.svelte +++ b/web/src/routes/admin/+layout.svelte @@ -1,29 +1,65 @@ -
- -
-
+ -
+ + diff --git a/web/src/routes/stores.ts b/web/src/routes/stores.ts new file mode 100644 index 0000000..03aceab --- /dev/null +++ b/web/src/routes/stores.ts @@ -0,0 +1,3 @@ +import { writable } from 'svelte/store'; + +export const theme = writable<'light' | 'dark'>('dark'); From 863254f846ac5e0d571c7115c322dbf88bf72932 Mon Sep 17 00:00:00 2001 From: orosmatthew Date: Sat, 26 Aug 2023 11:52:57 -0400 Subject: [PATCH 06/10] Add icons --- web/src/routes/admin/+layout.svelte | 32 +++++++++++++++---- web/src/routes/admin/+page.server.ts | 3 ++ web/src/routes/admin/+page.svelte | 2 +- web/src/routes/admin/contests/+page.svelte | 2 +- web/src/routes/admin/problems/+page.svelte | 2 +- web/src/routes/admin/reviews/+page.svelte | 2 +- web/src/routes/admin/scoreboard/+page.svelte | 2 +- web/src/routes/admin/submissions/+page.svelte | 2 +- web/src/routes/admin/teams/+page.svelte | 2 +- 9 files changed, 35 insertions(+), 14 deletions(-) create mode 100644 web/src/routes/admin/+page.server.ts diff --git a/web/src/routes/admin/+layout.svelte b/web/src/routes/admin/+layout.svelte index bf6d7ee..7123f72 100644 --- a/web/src/routes/admin/+layout.svelte +++ b/web/src/routes/admin/+layout.svelte @@ -18,13 +18,31 @@
diff --git a/web/src/routes/admin/+page.server.ts b/web/src/routes/admin/+page.server.ts new file mode 100644 index 0000000..5291cad --- /dev/null +++ b/web/src/routes/admin/+page.server.ts @@ -0,0 +1,3 @@ +import type { PageServerLoad } from './$types'; + +export const load = (async () => {}) satisfies PageServerLoad; diff --git a/web/src/routes/admin/+page.svelte b/web/src/routes/admin/+page.svelte index 8d44d44..e10d5d9 100644 --- a/web/src/routes/admin/+page.svelte +++ b/web/src/routes/admin/+page.svelte @@ -2,4 +2,4 @@ Admin Home -

Admin Home

+

Dashboard

diff --git a/web/src/routes/admin/contests/+page.svelte b/web/src/routes/admin/contests/+page.svelte index 8896a44..042d4a8 100644 --- a/web/src/routes/admin/contests/+page.svelte +++ b/web/src/routes/admin/contests/+page.svelte @@ -8,7 +8,7 @@ Contests -

Contests

+

Contests

diff --git a/web/src/routes/admin/problems/+page.svelte b/web/src/routes/admin/problems/+page.svelte index 9b590cb..bbe6f58 100644 --- a/web/src/routes/admin/problems/+page.svelte +++ b/web/src/routes/admin/problems/+page.svelte @@ -8,7 +8,7 @@ Problems -

Problems

+

Problems

diff --git a/web/src/routes/admin/reviews/+page.svelte b/web/src/routes/admin/reviews/+page.svelte index 9aa1dc9..68298da 100644 --- a/web/src/routes/admin/reviews/+page.svelte +++ b/web/src/routes/admin/reviews/+page.svelte @@ -27,7 +27,7 @@ Reviews -

Reviews

+

Reviews

{#if updating} diff --git a/web/src/routes/admin/scoreboard/+page.svelte b/web/src/routes/admin/scoreboard/+page.svelte index ecc9b02..71691c7 100644 --- a/web/src/routes/admin/scoreboard/+page.svelte +++ b/web/src/routes/admin/scoreboard/+page.svelte @@ -25,7 +25,7 @@ Admin Scoreboard -

Admin Scoreboards

+

Admin Scoreboards

{#if updating} diff --git a/web/src/routes/admin/submissions/+page.svelte b/web/src/routes/admin/submissions/+page.svelte index 138c672..fe2bfac 100644 --- a/web/src/routes/admin/submissions/+page.svelte +++ b/web/src/routes/admin/submissions/+page.svelte @@ -31,7 +31,7 @@ Submissions -

Submissions

+

Submissions

diff --git a/web/src/routes/admin/teams/+page.svelte b/web/src/routes/admin/teams/+page.svelte index 0a2cd2e..d826adb 100644 --- a/web/src/routes/admin/teams/+page.svelte +++ b/web/src/routes/admin/teams/+page.svelte @@ -16,7 +16,7 @@ Teams -

Teams

+

Teams

{#if form && !form.success}
Invalid action
From bb722b437e59ae518eca616428f996a51507ad7d Mon Sep 17 00:00:00 2001 From: orosmatthew Date: Sat, 26 Aug 2023 12:30:22 -0400 Subject: [PATCH 07/10] [web] Improve modals --- web/src/lib/ConfirmModal.svelte | 76 +++++++++++++++++++ web/src/lib/FormAlert.svelte | 46 +++++++++++ web/src/routes/admin/contests/+page.svelte | 8 +- .../admin/contests/[contestId]/+page.svelte | 21 ++--- .../routes/admin/contests/create/+page.svelte | 9 +-- .../admin/diff/[submissionId]/+page.svelte | 7 +- web/src/routes/admin/problems/+page.svelte | 2 +- .../admin/problems/[problemId]/+page.svelte | 11 ++- web/src/routes/admin/reviews/+page.svelte | 2 +- web/src/routes/admin/scoreboard/+page.svelte | 2 +- .../submissions/[submissionId]/+page.svelte | 11 ++- web/src/routes/admin/teams/+page.svelte | 2 +- .../routes/admin/teams/[teamId]/+page.svelte | 11 ++- web/svelte.config.js | 3 + 14 files changed, 175 insertions(+), 36 deletions(-) create mode 100644 web/src/lib/ConfirmModal.svelte create mode 100644 web/src/lib/FormAlert.svelte diff --git a/web/src/lib/ConfirmModal.svelte b/web/src/lib/ConfirmModal.svelte new file mode 100644 index 0000000..c5c029e --- /dev/null +++ b/web/src/lib/ConfirmModal.svelte @@ -0,0 +1,76 @@ + + + diff --git a/web/src/lib/FormAlert.svelte b/web/src/lib/FormAlert.svelte new file mode 100644 index 0000000..b979441 --- /dev/null +++ b/web/src/lib/FormAlert.svelte @@ -0,0 +1,46 @@ + + +{#if ($page.form !== null && dismissed === false) || (manualPopup === true && dismissed === false)} +
+ {success ? 'Success' : message ?? 'Unknown error'} +
+{/if} diff --git a/web/src/routes/admin/contests/+page.svelte b/web/src/routes/admin/contests/+page.svelte index 042d4a8..74760e4 100644 --- a/web/src/routes/admin/contests/+page.svelte +++ b/web/src/routes/admin/contests/+page.svelte @@ -8,12 +8,10 @@ Contests -

Contests

+

Contests

-
-
- Create -
+
+ Create
diff --git a/web/src/routes/admin/contests/[contestId]/+page.svelte b/web/src/routes/admin/contests/[contestId]/+page.svelte index 8800d79..7179066 100644 --- a/web/src/routes/admin/contests/[contestId]/+page.svelte +++ b/web/src/routes/admin/contests/[contestId]/+page.svelte @@ -1,26 +1,29 @@ {data.name} + +

{data.name}

+ + {#if data.activeTeams !== 0}
In Progress
{/if} -{#if form && !form.success} -
An error occured
-{/if} -
All Contests @@ -28,12 +31,12 @@
{ - if (!confirm('Are you sure?')) { + use:enhance={async ({ cancel }) => { + if ((await confirmModal.prompt('Are you sure?')) !== true) { cancel(); } return async ({ update }) => { - update(); + await update(); }; }} > diff --git a/web/src/routes/admin/contests/create/+page.svelte b/web/src/routes/admin/contests/create/+page.svelte index 3fb99a7..0b81b21 100644 --- a/web/src/routes/admin/contests/create/+page.svelte +++ b/web/src/routes/admin/contests/create/+page.svelte @@ -1,6 +1,7 @@ -

{data.problemData.friendlyName}

+ + +

+ Problem - {data.problemData.friendlyName} +

{#if error}
diff --git a/web/src/routes/admin/reviews/+page.svelte b/web/src/routes/admin/reviews/+page.svelte index 68298da..e431469 100644 --- a/web/src/routes/admin/reviews/+page.svelte +++ b/web/src/routes/admin/reviews/+page.svelte @@ -27,7 +27,7 @@ Reviews -

Reviews

+

Reviews

{#if updating} diff --git a/web/src/routes/admin/scoreboard/+page.svelte b/web/src/routes/admin/scoreboard/+page.svelte index 71691c7..5d84d01 100644 --- a/web/src/routes/admin/scoreboard/+page.svelte +++ b/web/src/routes/admin/scoreboard/+page.svelte @@ -25,7 +25,7 @@ Admin Scoreboard -

Admin Scoreboards

+

Admin Scoreboards

{#if updating} diff --git a/web/src/routes/admin/submissions/[submissionId]/+page.svelte b/web/src/routes/admin/submissions/[submissionId]/+page.svelte index 825779a..75ab311 100644 --- a/web/src/routes/admin/submissions/[submissionId]/+page.svelte +++ b/web/src/routes/admin/submissions/[submissionId]/+page.svelte @@ -5,6 +5,7 @@ import 'diff2html/bundles/css/diff2html.min.css'; import { enhance } from '$app/forms'; import { stretchTextarea } from '$lib/util'; + import ConfirmModal from '$lib/ConfirmModal.svelte'; export let data: PageData; export let form: Actions; @@ -25,12 +26,16 @@ } } }); + + let confirmModal: ConfirmModal; Submission + +

Submission

{#if form && !form.success} @@ -45,12 +50,12 @@ { - if (!confirm('Are you sure?')) { + use:enhance={async ({ cancel }) => { + if ((await confirmModal.prompt('Are you sure?')) !== true) { cancel(); } return async ({ update }) => { - update(); + await update(); }; }} > diff --git a/web/src/routes/admin/teams/+page.svelte b/web/src/routes/admin/teams/+page.svelte index d826adb..015f601 100644 --- a/web/src/routes/admin/teams/+page.svelte +++ b/web/src/routes/admin/teams/+page.svelte @@ -16,7 +16,7 @@ Teams -

Teams

+

Teams

{#if form && !form.success}
Invalid action
diff --git a/web/src/routes/admin/teams/[teamId]/+page.svelte b/web/src/routes/admin/teams/[teamId]/+page.svelte index 6d26c86..0adde55 100644 --- a/web/src/routes/admin/teams/[teamId]/+page.svelte +++ b/web/src/routes/admin/teams/[teamId]/+page.svelte @@ -1,5 +1,6 @@ Team + +

{data.team.name}

@@ -32,12 +37,12 @@ { - if (!confirm('Are you sure?')) { + use:enhance={async ({ cancel }) => { + if ((await confirmModal.prompt('Are you sure?')) !== true) { cancel(); } return async ({ update }) => { - update(); + await update(); }; }} > diff --git a/web/svelte.config.js b/web/svelte.config.js index e7737a8..2c94519 100644 --- a/web/svelte.config.js +++ b/web/svelte.config.js @@ -7,6 +7,9 @@ const config = { kit: { adapter: adapter() + }, + vitePlugin: { + inspector: true } }; From 8e4564fe48f5eedc1fb4dceb505aaee708d56bf1 Mon Sep 17 00:00:00 2001 From: orosmatthew Date: Sat, 26 Aug 2023 12:32:39 -0400 Subject: [PATCH 08/10] [web] Fix titles --- web/src/routes/admin/+page.svelte | 2 +- web/src/routes/admin/scoreboard/+page.svelte | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/web/src/routes/admin/+page.svelte b/web/src/routes/admin/+page.svelte index e10d5d9..e36b77c 100644 --- a/web/src/routes/admin/+page.svelte +++ b/web/src/routes/admin/+page.svelte @@ -1,5 +1,5 @@ - Admin Home + Dashboard

Dashboard

diff --git a/web/src/routes/admin/scoreboard/+page.svelte b/web/src/routes/admin/scoreboard/+page.svelte index 5d84d01..6281b20 100644 --- a/web/src/routes/admin/scoreboard/+page.svelte +++ b/web/src/routes/admin/scoreboard/+page.svelte @@ -22,7 +22,7 @@ - Admin Scoreboard + Admin Scoreboards

Admin Scoreboards

From 711573cd209245e50aae8935a134aedd9c9132c3 Mon Sep 17 00:00:00 2001 From: orosmatthew Date: Sat, 26 Aug 2023 13:46:59 -0400 Subject: [PATCH 09/10] [sandbox] Formatting and scripts --- sandbox/.gitignore | 2 +- sandbox/.prettierignore | 2 ++ sandbox/.prettierrc | 3 +-- sandbox/Dockerfile | 2 +- sandbox/package-lock.json | 16 ++++++++++++++++ sandbox/package.json | 5 ++++- sandbox/tsconfig.json | 2 +- 7 files changed, 26 insertions(+), 6 deletions(-) create mode 100644 sandbox/.prettierignore diff --git a/sandbox/.gitignore b/sandbox/.gitignore index a0d218e..c27e9ba 100644 --- a/sandbox/.gitignore +++ b/sandbox/.gitignore @@ -1,3 +1,3 @@ node_modules -dist +build .env \ No newline at end of file diff --git a/sandbox/.prettierignore b/sandbox/.prettierignore new file mode 100644 index 0000000..48912d2 --- /dev/null +++ b/sandbox/.prettierignore @@ -0,0 +1,2 @@ +build +node_modules \ No newline at end of file diff --git a/sandbox/.prettierrc b/sandbox/.prettierrc index c125846..86d4b8b 100644 --- a/sandbox/.prettierrc +++ b/sandbox/.prettierrc @@ -3,6 +3,5 @@ "useTabs": true, "singleQuote": true, "trailingComma": "none", - "printWidth": 100, - "pluginSearchDirs": ["."] + "printWidth": 100 } diff --git a/sandbox/Dockerfile b/sandbox/Dockerfile index 4974209..953e6d3 100644 --- a/sandbox/Dockerfile +++ b/sandbox/Dockerfile @@ -36,4 +36,4 @@ COPY . . RUN npm run build -CMD ["node", "dist"] \ No newline at end of file +CMD ["node", "build"] \ No newline at end of file diff --git a/sandbox/package-lock.json b/sandbox/package-lock.json index 53d3775..4d35f20 100644 --- a/sandbox/package-lock.json +++ b/sandbox/package-lock.json @@ -17,6 +17,7 @@ }, "devDependencies": { "@types/fs-extra": "^11.0.1", + "prettier": "^3.0.2", "typescript": "^5.1.6" } }, @@ -119,6 +120,21 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/prettier": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.2.tgz", + "integrity": "sha512-o2YR9qtniXvwEZlOKbveKfDQVyqxbEIWn48Z8m3ZJjBjcCmUy3xZGIv+7AkaeuaTr6yPXJjwv07ZWlsWbEy1rQ==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/simple-git": { "version": "3.19.1", "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.19.1.tgz", diff --git a/sandbox/package.json b/sandbox/package.json index ca05f52..0fe1bdf 100644 --- a/sandbox/package.json +++ b/sandbox/package.json @@ -5,12 +5,15 @@ "main": "index.js", "type": "module", "scripts": { - "build": "tsc" + "build": "tsc", + "format": "prettier --write .", + "start": "node build" }, "author": "", "license": "ISC", "devDependencies": { "@types/fs-extra": "^11.0.1", + "prettier": "^3.0.2", "typescript": "^5.1.6" }, "dependencies": { diff --git a/sandbox/tsconfig.json b/sandbox/tsconfig.json index 6694831..7e9b0cb 100644 --- a/sandbox/tsconfig.json +++ b/sandbox/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "module": "NodeNext", "sourceMap": true, - "outDir": "./dist", + "outDir": "./build", "esModuleInterop": true, "strict": true }, From 4b3e40decd117e2a408d64ad97f72dd0a7b5a424 Mon Sep 17 00:00:00 2001 From: orosmatthew Date: Sat, 26 Aug 2023 14:19:03 -0400 Subject: [PATCH 10/10] Add dark diff theme --- web/package-lock.json | 46 ++++++++++++---- web/package.json | 1 + .../submissions/[submissionId]/+page.svelte | 53 ++++++++++++++++++- 3 files changed, 88 insertions(+), 12 deletions(-) diff --git a/web/package-lock.json b/web/package-lock.json index c487cbc..754c760 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -40,6 +40,7 @@ "eslint-config-prettier": "^9.0.0", "prettier": "^3.0.2", "prettier-plugin-svelte": "^3.0.3", + "sass": "^1.66.1", "svelte": "^4.2.0", "svelte-check": "^3.5.0", "tslib": "^2.6.2", @@ -1235,7 +1236,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, + "devOptional": true, "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -1313,7 +1314,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, + "devOptional": true, "engines": { "node": ">=8" } @@ -1364,7 +1365,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, + "devOptional": true, "dependencies": { "fill-range": "^7.0.1" }, @@ -1430,7 +1431,7 @@ "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, + "devOptional": true, "funding": [ { "type": "individual", @@ -1457,7 +1458,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, + "devOptional": true, "dependencies": { "is-glob": "^4.0.1" }, @@ -1998,7 +1999,7 @@ "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, + "devOptional": true, "dependencies": { "to-regex-range": "^5.0.1" }, @@ -2263,6 +2264,12 @@ "node": ">= 4" } }, + "node_modules/immutable": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.4.tgz", + "integrity": "sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==", + "devOptional": true + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -2314,7 +2321,7 @@ "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, + "devOptional": true, "dependencies": { "binary-extensions": "^2.0.0" }, @@ -2383,7 +2390,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, + "devOptional": true, "engines": { "node": ">=0.12.0" } @@ -2754,7 +2761,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, + "devOptional": true, "engines": { "node": ">=0.10.0" } @@ -3120,7 +3127,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, + "devOptional": true, "dependencies": { "picomatch": "^2.2.1" }, @@ -3266,6 +3273,23 @@ "rimraf": "bin.js" } }, + "node_modules/sass": { + "version": "1.66.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.66.1.tgz", + "integrity": "sha512-50c+zTsZOJVgFfTgwwEzkjA3/QACgdNsKueWPyAR0mRINIvLAStVQBbPg14iuqEQ74NPDbXzJARJ/O4SI1zftA==", + "devOptional": true, + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -3671,7 +3695,7 @@ "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, + "devOptional": true, "dependencies": { "is-number": "^7.0.0" }, diff --git a/web/package.json b/web/package.json index 2b3e5c5..26a6b36 100644 --- a/web/package.json +++ b/web/package.json @@ -26,6 +26,7 @@ "eslint-config-prettier": "^9.0.0", "prettier": "^3.0.2", "prettier-plugin-svelte": "^3.0.3", + "sass": "^1.66.1", "svelte": "^4.2.0", "svelte-check": "^3.5.0", "tslib": "^2.6.2", diff --git a/web/src/routes/admin/submissions/[submissionId]/+page.svelte b/web/src/routes/admin/submissions/[submissionId]/+page.svelte index 75ab311..38b230f 100644 --- a/web/src/routes/admin/submissions/[submissionId]/+page.svelte +++ b/web/src/routes/admin/submissions/[submissionId]/+page.svelte @@ -115,5 +115,56 @@

Output

Diff

-
+
{/if} + +