diff --git a/.eslintignore b/.eslintignore index 01133bc..86fe674 100644 --- a/.eslintignore +++ b/.eslintignore @@ -2,3 +2,4 @@ dist processes.config.js node_modules coverage +vitest.config.mts diff --git a/.gitignore b/.gitignore index 607d457..a553f34 100644 --- a/.gitignore +++ b/.gitignore @@ -55,5 +55,6 @@ test/**/*.js.map #env .env.local .env.development +.envrc database.sqlite diff --git a/package-lock.json b/package-lock.json index ea999a8..ed5687a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -44,12 +44,14 @@ "express": "^4.20.0", "express-session": "^1.18.0", "method-override": "^3.0.0", + "missing.css": "^1.1.3", "passport": "^0.7.0", "passport-github": "^1.1.0", "passport-oauth2": "^1.8.0", "reflect-metadata": "^0.2.2", "sqlite3": "^5.1.7", - "uuid": "^10.0.0" + "uuid": "^10.0.0", + "victormono": "^1.5.6" }, "devDependencies": { "@swc/core": "^1.7.24", @@ -114,33 +116,30 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", - "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", - "dev": true, + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.7.tgz", + "integrity": "sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", - "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", - "dev": true, + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.7.tgz", + "integrity": "sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.6.tgz", - "integrity": "sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==", - "dev": true, + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.7.tgz", + "integrity": "sha512-aZn7ETtQsjjGG5HruveUK06cU3Hljuhd9Iojm4M8WWv3wLE6OkE5PWbDUkItmMgegmccaITudyuW5RPYrYlgWw==", "license": "MIT", "dependencies": { - "@babel/types": "^7.25.6" + "@babel/types": "^7.25.7" }, "bin": { "parser": "bin/babel-parser.js" @@ -150,14 +149,13 @@ } }, "node_modules/@babel/types": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.6.tgz", - "integrity": "sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==", - "dev": true, + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.7.tgz", + "integrity": "sha512-vwIVdXG+j+FOpkwqHRcBgHLYNL7XMkufrlaFvL9o6Ai9sJn9+PdyIL5qa0XzTZw084c+u9LOls53eoZWP/W5WQ==", "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.24.8", - "@babel/helper-validator-identifier": "^7.24.7", + "@babel/helper-string-parser": "^7.25.7", + "@babel/helper-validator-identifier": "^7.25.7", "to-fast-properties": "^2.0.0" }, "engines": { @@ -3170,6 +3168,35 @@ "@types/estree": "^1.0.0" } }, + "node_modules/@vue/compiler-sfc": { + "version": "2.7.16", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.16.tgz", + "integrity": "sha512-KWhJ9k5nXuNtygPU7+t1rX6baZeqOYLEforUPjgNDBnLicfHCoi48H87Q8XyLZOrNNsmhuwKqtpDQWjEFe6Ekg==", + "dependencies": { + "@babel/parser": "^7.23.5", + "postcss": "^8.4.14", + "source-map": "^0.6.1" + }, + "optionalDependencies": { + "prettier": "^1.18.2 || ^2.0.0" + } + }, + "node_modules/@vue/compiler-sfc/node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "license": "MIT", + "optional": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -3333,6 +3360,12 @@ } } }, + "node_modules/animejs": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/animejs/-/animejs-3.2.2.tgz", + "integrity": "sha512-Ao95qWLpDPXXM+WrmwcKbl6uNlC5tjnowlaRYtuVDHHoygjtIPfDUoK9NthrlZsQSKjZXlmji2TrBUAVbiH0LQ==", + "license": "MIT" + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -3483,6 +3516,14 @@ "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", "license": "MIT" }, + "node_modules/async-validator": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/async-validator/-/async-validator-1.8.5.tgz", + "integrity": "sha512-tXBM+1m056MAX0E8TL2iCjg8WvSyXu0Zc8LNtYqrVeyoL3+esHRZ4SieE9fKQyyU09uONjnMEjrNBMqT0mbvmA==", + "dependencies": { + "babel-runtime": "6.x" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -3490,6 +3531,22 @@ "dev": true, "license": "MIT" }, + "node_modules/babel-helper-vue-jsx-merge-props": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-2.0.3.tgz", + "integrity": "sha512-gsLiKK7Qrb7zYJNgiXKpXblxbV5ffSwR0f5whkPAaBAR4fhi6bwRZxX9wBlIc5M/v8CCkXUbXZL4N/nSE97cqg==", + "license": "MIT" + }, + "node_modules/babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==", + "license": "MIT", + "dependencies": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -3542,6 +3599,12 @@ "node": ">=6.0.0" } }, + "node_modules/bezier-easing": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/bezier-easing/-/bezier-easing-2.1.0.tgz", + "integrity": "sha512-gbIqZ/eslnUFC1tjEvtz0sgx+xTK20wDnYMIA27VA04R7w6xxXQPZDbibjA9DTWZRA2CXtwHykkVzlCaAJAZig==", + "license": "MIT" + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -4017,6 +4080,12 @@ "node": ">=12" } }, + "node_modules/codemirror": { + "version": "5.65.18", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.18.tgz", + "integrity": "sha512-Gaz4gHnkbHMGgahNt3CA5HBk5lLQBqmD/pBgeB4kQU6OedZmqMBjlRF0LSrp2tJ4wlLNPm2FfaUd1pDy0mdlpA==", + "license": "MIT" + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -4231,6 +4300,14 @@ "dev": true, "license": "MIT" }, + "node_modules/core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", + "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", + "hasInstallScript": true, + "license": "MIT" + }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -4289,6 +4366,12 @@ "node": ">= 8" } }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, "node_modules/date-format": { "version": "4.0.14", "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz", @@ -4355,6 +4438,15 @@ "dev": true, "license": "MIT" }, + "node_modules/deepmerge": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-1.5.2.tgz", + "integrity": "sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -4438,6 +4530,12 @@ "node": ">=0.3.1" } }, + "node_modules/diff-match-patch": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz", + "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==", + "license": "Apache-2.0" + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -4463,6 +4561,12 @@ "node": ">=6.0.0" } }, + "node_modules/dom-confetti": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/dom-confetti/-/dom-confetti-0.1.2.tgz", + "integrity": "sha512-6Zo1pJEbEW1/GPgmk7uGWnPsoZ1E1vF7pmrPARnN3tp8L3ReMr/SIyLl6E8mGbu4DermzDWXfa+be77HehH2MA==", + "license": "MIT" + }, "node_modules/dot-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", @@ -4550,6 +4654,23 @@ "node": ">=0.10.0" } }, + "node_modules/element-ui": { + "version": "2.15.14", + "resolved": "https://registry.npmjs.org/element-ui/-/element-ui-2.15.14.tgz", + "integrity": "sha512-2v9fHL0ZGINotOlRIAJD5YuVB8V7WKxrE9Qy7dXhRipa035+kF7WuU/z+tEmLVPBcJ0zt8mOu1DKpWcVzBK8IA==", + "license": "MIT", + "dependencies": { + "async-validator": "~1.8.1", + "babel-helper-vue-jsx-merge-props": "^2.0.0", + "deepmerge": "^1.2.0", + "normalize-wheel": "^1.0.1", + "resize-observer-polyfill": "^1.5.0", + "throttle-debounce": "^1.0.1" + }, + "peerDependencies": { + "vue": "^2.5.17" + } + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -5782,6 +5903,12 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, + "node_modules/granim": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/granim/-/granim-2.0.0.tgz", + "integrity": "sha512-aqa79K49ndjoUBtpYzlO8sKcuVQED+5axvX0SveqTLDR+Fa2G42AGntuQ36ysCFOWGVkWCLfHowFwk+D/9rGDg==", + "license": "MIT" + }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -6053,6 +6180,15 @@ "node": ">= 4" } }, + "node_modules/immutable": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", + "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -6911,6 +7047,12 @@ "node": ">= 8" } }, + "node_modules/missing.css": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/missing.css/-/missing.css-1.1.3.tgz", + "integrity": "sha512-dkGzliE9Zcv9tGPvuIHy1lpMc4Y5QAc+svF7Nqi+EMl4taRC5HfjARg55YC2jC9cbqOwX0qOUltUxqn57YIQiw==", + "license": "BSD 2-Clause" + }, "node_modules/mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", @@ -6969,7 +7111,6 @@ "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "dev": true, "funding": [ { "type": "github", @@ -7125,6 +7266,12 @@ "node": ">=0.10.0" } }, + "node_modules/normalize-wheel": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/normalize-wheel/-/normalize-wheel-1.0.1.tgz", + "integrity": "sha512-1OnlAPZ3zgrk8B91HyRj+eVv+kS5u+Z0SCsak6Xil/kmgEia50ga7zfkumayonZrImffAxPU/5WcyGhzetHNPA==", + "license": "BSD-3-Clause" + }, "node_modules/npm-run-path": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", @@ -7568,7 +7715,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", - "dev": true, "license": "ISC" }, "node_modules/picomatch": { @@ -7609,7 +7755,6 @@ "version": "8.4.45", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.45.tgz", "integrity": "sha512-7KTLTdzdZZYscUc65XmjFiB73vBhBfbPztCYdUNvlaso9PrzjzcmjqBPR0lNGkcVlcO4BjiO5rK/qNz+XAen1Q==", - "dev": true, "funding": [ { "type": "opencollective", @@ -7885,6 +8030,12 @@ "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", "license": "Apache-2.0" }, + "node_modules/regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", + "license": "MIT" + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -7903,6 +8054,12 @@ "node": ">=0.10.0" } }, + "node_modules/resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==", + "license": "MIT" + }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -8148,6 +8305,60 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, + "node_modules/sass": { + "version": "1.79.4", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.79.4.tgz", + "integrity": "sha512-K0QDSNPXgyqO4GZq2HO5Q70TLxTH6cIT59RdoCHMivrC8rqzaTw5ab9prjz9KUN1El4FLXrBXJhik61JR4HcGg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "chokidar": "^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/sass/node_modules/chokidar": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", + "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/sass/node_modules/readdirp": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.1.tgz", + "integrity": "sha512-GkMg9uOTpIWWKbSsgwb5fA4EavTR+SG/PMPoAY8hkhHfEEY0/vqljY+XHqtDf2cr2IJtoNRDbrrEpZUiZCkYRw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/semver": { "version": "7.6.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", @@ -8600,7 +8811,6 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -8610,7 +8820,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -9176,6 +9385,15 @@ "node": ">=0.8" } }, + "node_modules/throttle-debounce": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-1.1.0.tgz", + "integrity": "sha512-XH8UiPCQcWNuk2LYePibW/4qL97+ZQ1AN3FNXwZRBNPPowo/NRU5fAlDCSNBJIYCKbioZfuYtMhG4quqoJhVzg==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/tinybench": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", @@ -9217,7 +9435,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -9453,6 +9670,12 @@ "node": ">= 0.6" } }, + "node_modules/typed.js": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/typed.js/-/typed.js-2.1.0.tgz", + "integrity": "sha512-bDuXEf7YcaKN4g08NMTUM6G90XU25CK3bh6U0THC/Mod/QPKlEt9g/EjvbYB8x2Qwr2p6J6I3NrsoYaVnY6wsQ==", + "license": "MIT" + }, "node_modules/typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -9888,6 +10111,27 @@ "node": ">= 0.8" } }, + "node_modules/victormono": { + "version": "1.5.6", + "resolved": "https://registry.npmjs.org/victormono/-/victormono-1.5.6.tgz", + "integrity": "sha512-ZZjEdcKAVRvNdL1tpq99KKJYL8dtA5YuGH33EKk4+7UeXH3HuZR1MBjYR5BB+MKogWvHri50a0vFwVPScPyiUg==", + "license": "MIT", + "dependencies": { + "animejs": "^3.2.1", + "core-js": "^2.6.12", + "dom-confetti": "^0.1.2", + "element-ui": "^2.14.1", + "granim": "^2.0.0", + "typed.js": "^2.0.11", + "vue": "^2.6.12", + "vue-codemirror": "^4.0.6", + "vue-faq-accordion": "^1.6.2", + "vue-scrollTo": "^2.4.1", + "vue-social-sharing": "^2.4.6", + "vue-tweet-embed": "^2.4.0", + "vue-twentytwenty": "^0.6.2" + } + }, "node_modules/vite": { "version": "5.4.3", "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.3.tgz", @@ -10086,6 +10330,83 @@ "dev": true, "license": "MIT" }, + "node_modules/vue": { + "version": "2.7.16", + "resolved": "https://registry.npmjs.org/vue/-/vue-2.7.16.tgz", + "integrity": "sha512-4gCtFXaAA3zYZdTp5s4Hl2sozuySsgz4jy1EnpBHNfpMa9dK1ZCG7viqBPCwXtmgc8nHqUsAu3G4gtmXkkY3Sw==", + "deprecated": "Vue 2 has reached EOL and is no longer actively maintained. See https://v2.vuejs.org/eol/ for more details.", + "license": "MIT", + "dependencies": { + "@vue/compiler-sfc": "2.7.16", + "csstype": "^3.1.0" + } + }, + "node_modules/vue-codemirror": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/vue-codemirror/-/vue-codemirror-4.0.6.tgz", + "integrity": "sha512-ilU7Uf0mqBNSSV3KT7FNEeRIxH4s1fmpG4TfHlzvXn0QiQAbkXS9lLfwuZpaBVEnpP5CSE62iGJjoliTuA8poQ==", + "license": "MIT", + "dependencies": { + "codemirror": "^5.41.0", + "diff-match-patch": "^1.0.0" + }, + "engines": { + "node": ">= 4.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/vue-faq-accordion": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/vue-faq-accordion/-/vue-faq-accordion-1.6.2.tgz", + "integrity": "sha512-PXFgeYQiXBe6fYERfIn44bFYUYsP+TiZ+qBH/KQKvXck85neCZvQiJN8IHwMLSC41fDdBnQvBIi6V9oO/wl/zg==", + "license": "MIT", + "dependencies": { + "vue2-transitions": "^0.3.0" + } + }, + "node_modules/vue-scrollTo": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/vue-scrollTo/-/vue-scrollTo-2.4.1.tgz", + "integrity": "sha512-l/f3ApUTnkGdElQBDhtDkiVkwO8uewMPY+FyOUnz94WcoqiOszsHLty1iGaX0V2y8fLz/hCQ/8XaNPjZ0xqa6A==", + "deprecated": "The package has been renamed to vue-scrollto (lowercase T)", + "license": "MIT", + "dependencies": { + "bezier-easing": "^2.0.3" + } + }, + "node_modules/vue-social-sharing": { + "version": "2.4.7", + "resolved": "https://registry.npmjs.org/vue-social-sharing/-/vue-social-sharing-2.4.7.tgz", + "integrity": "sha512-X70bulEnjSHvakf8NaLhuKTuUS4yKzdhYjX/aJ4dPVIXWaoXH8w+QODuM3C6c3fPaiNqgR19VrDyWB7EUGUzPQ==", + "license": "MIT", + "dependencies": { + "vue": "^2.2.4" + }, + "engines": { + "node": ">= 6.0" + } + }, + "node_modules/vue-tweet-embed": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/vue-tweet-embed/-/vue-tweet-embed-2.4.0.tgz", + "integrity": "sha512-bjViatv0priR1dTEPJpRyWigWGUTUC28VT/sWTaZE+RBWuj/XZvOU5Hzk+O8Mue2dBCAHJrRpoO1VKlcgmHohg==", + "license": "MIT", + "peerDependencies": { + "vue": "^2.2.0" + } + }, + "node_modules/vue-twentytwenty": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/vue-twentytwenty/-/vue-twentytwenty-0.6.2.tgz", + "integrity": "sha512-JKwxw/p8e4wab0xPbHhj3np2ltNUh9YAaiJYlJCRWEpBig3BzgJwozLJnm5Y/BPUuy46R61o6tr0mNv66CZHQQ==", + "license": "MIT" + }, + "node_modules/vue2-transitions": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/vue2-transitions/-/vue2-transitions-0.3.0.tgz", + "integrity": "sha512-m1ad8K8kufqiEhj5gXHkkqOioI5sW0FaMbRiO0Tv2WFfGbO2eIKrfkFiO3HPQtMJboimaLCN4p/zL81clLbG4w==", + "license": "MIT" + }, "node_modules/webpack-virtual-modules": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", diff --git a/package.json b/package.json index 3328a99..e18442d 100644 --- a/package.json +++ b/package.json @@ -53,12 +53,14 @@ "express": "^4.20.0", "express-session": "^1.18.0", "method-override": "^3.0.0", + "missing.css": "^1.1.3", "passport": "^0.7.0", "passport-github": "^1.1.0", "passport-oauth2": "^1.8.0", "reflect-metadata": "^0.2.2", "sqlite3": "^5.1.7", - "uuid": "^10.0.0" + "uuid": "^10.0.0", + "victormono": "^1.5.6" }, "devDependencies": { "@swc/core": "^1.7.24", diff --git a/src/Server.ts b/src/Server.ts index ee01f20..35ccf5c 100644 --- a/src/Server.ts +++ b/src/Server.ts @@ -14,7 +14,7 @@ import "./protocols/GthubProtocol"; @Configuration({ ...config, acceptMimes: ["application/json"], - httpPort: process.env.PORT || 8083, + httpPort: process.env.PORT || "127.0.0.1:8081", httpsPort: false, // CHANGE disableComponentsScan: false, ajv: { @@ -30,6 +30,17 @@ import "./protocols/GthubProtocol"; specVersion: "3.0.1" } ], + statics: { + "/static/victormono": { + root: join(__dirname, "../node_modules/victormono/dist"), + }, + "/static/missing": { + root: join(__dirname, "../node_modules/missing.css/dist") + }, + "/static": { + root: join(__dirname, "../static") + } + }, componentsScan: [`./protocols/*.ts`, `./services/*.ts`], passport: { userInfoModel: User diff --git a/src/controllers/pages/app/AppController.ts b/src/controllers/pages/app/AppController.ts new file mode 100644 index 0000000..1a75d8f --- /dev/null +++ b/src/controllers/pages/app/AppController.ts @@ -0,0 +1,19 @@ +import { Controller } from "@tsed/di"; +import { HeaderParams } from "@tsed/platform-params"; +import { View } from "@tsed/platform-views"; +import { Hidden, Get, Returns } from "@tsed/schema"; + +@Hidden() +@Controller("/app") +export class AppController { + @Get("/") + @View("app/send.ejs") + @(Returns(200, String).ContentType("text/html")) + get(@HeaderParams("x-forwarded-proto") protocol: string, @HeaderParams("host") host: string) { + const hostUrl = `${protocol || "http"}://${host}`; + + return { + BASE_URL: hostUrl + }; + } +} diff --git a/src/controllers/pages/index.ts b/src/controllers/pages/index.ts index ba26d4c..e558cf4 100644 --- a/src/controllers/pages/index.ts +++ b/src/controllers/pages/index.ts @@ -3,3 +3,4 @@ */ export * from "./IndexController"; +export * from "./app/AppController"; diff --git a/src/controllers/rest/LinkController.ts b/src/controllers/rest/LinkController.ts index b87c32a..dc54d25 100644 --- a/src/controllers/rest/LinkController.ts +++ b/src/controllers/rest/LinkController.ts @@ -12,7 +12,7 @@ export class LinkController { constructor( private linkService: LinkService, // Inject LinkService private userService: UserService // Inject UserService - ) {} + ) { } @Post("/") @Summary("Create a new link") diff --git a/src/services/EncryptionService.ts b/src/services/EncryptionService.ts new file mode 100644 index 0000000..26191de --- /dev/null +++ b/src/services/EncryptionService.ts @@ -0,0 +1,23 @@ +import { Service } from "@tsed/di"; +import crypto from "crypto"; + +@Service() +export class EncryptionService { + private algorithm = 'aes-256-cbc'; + private key = crypto.randomBytes(32); + + async encrypt(text: string): Promise<{ encryptedText: string; iv: string }> { + const iv = crypto.randomBytes(16); + const cipher = crypto.createCipheriv(this.algorithm, this.key, iv); + let encrypted = cipher.update(text, 'utf8', 'hex'); + encrypted += cipher.final('hex'); + return { encryptedText: encrypted, iv: iv.toString('hex') }; + } + + async decrypt(encryptedText: string, iv: string): Promise { + const decipher = crypto.createDecipheriv(this.algorithm, this.key, Buffer.from(iv, 'hex')); + let decrypted = decipher.update(encryptedText, 'hex', 'utf8'); + decrypted += decipher.final('utf8'); + return decrypted; + } +} diff --git a/static/app.css b/static/app.css new file mode 100644 index 0000000..e258b9c --- /dev/null +++ b/static/app.css @@ -0,0 +1,5 @@ +html { + visibility: visible; + opacity: 1; + font-family: 'Victor Mono', monospace; +} \ No newline at end of file diff --git a/views/app/send.ejs b/views/app/send.ejs new file mode 100644 index 0000000..d13bc9c --- /dev/null +++ b/views/app/send.ejs @@ -0,0 +1,178 @@ + + + +<%- include('../layout/head.ejs', { title: 'Intended Link' }) %> + + + <%- include('../layout/header.ejs') %> +
+
+
+

Securely Share Your Secrets

+

Only the person with the account you specify will be able to decrypt your message.

+
+
+

+ + +

+

+ + +

+

+ + +

+

+ + +

+

+ +

+
+ +
+
+ + + +
+
+ + +
+
+
+
+ + + + <%- include('../layout/footer.ejs') %> + + + \ No newline at end of file diff --git a/views/layout/footer.ejs b/views/layout/footer.ejs new file mode 100644 index 0000000..09fec50 --- /dev/null +++ b/views/layout/footer.ejs @@ -0,0 +1,8 @@ + \ No newline at end of file diff --git a/views/layout/head.ejs b/views/layout/head.ejs new file mode 100644 index 0000000..2e3ab64 --- /dev/null +++ b/views/layout/head.ejs @@ -0,0 +1,25 @@ + + + + + + + <%= title %> + + + + + + \ No newline at end of file diff --git a/views/layout/header.ejs b/views/layout/header.ejs new file mode 100644 index 0000000..c6c1d9b --- /dev/null +++ b/views/layout/header.ejs @@ -0,0 +1,9 @@ + \ No newline at end of file diff --git a/views/swagger.ejs b/views/swagger.ejs index 44bac19..90d4fde 100644 --- a/views/swagger.ejs +++ b/views/swagger.ejs @@ -1,100 +1,32 @@ - - - - - - client - - - - -
-
- +<%- include('layout/head.ejs', { title: 'Intended Link' }) %> + + <%- include('layout/header.ejs') %> +
+

Intended Link

+

Securely share messages with verified recipients via OAuth.

+ +

How it works:

+
    +
  1.  Enter your secret message.
  2. +
  3.  Choose the service and username of the recipient.
  4. +
  5.  We generate a secure link that only the intended recipient can access.
  6. +
  7.  The recipient must authenticate with the specified account for the service in order to + access the message.
  8. +
+ +

Supported services

-
-
- - - + + + <%- include('layout/footer.ejs') %> + + + + \ No newline at end of file