diff --git a/.gitignore b/.gitignore index 6bdbe62..75b0373 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /node_modules/ /.idea/ +.env diff --git a/package.json b/package.json index 7435805..15de878 100644 --- a/package.json +++ b/package.json @@ -5,16 +5,20 @@ "type": "module", "main": "index.js", "scripts": { - "dev": "node --no-warnings=ExperimentalWarning --loader ts-node/esm src/main.ts" + "dev": "node --no-warnings=ExperimentalWarning --loader ts-node/esm src/main.ts", + "migrate": "prisma migrate dev" }, "keywords": [], "author": "", "license": "ISC", "packageManager": "pnpm@9.12.3+sha512.cce0f9de9c5a7c95bef944169cc5dfe8741abfb145078c0d508b868056848a87c81e626246cb60967cbd7fd29a6c062ef73ff840d96b3c86c40ac92cf4a813ee", "dependencies": { + "@fastify/multipart": "^9.0.1", + "@prisma/client": "6.0.0", + "dotenv": "^16.4.5", "fastify": "^5.1.0", - "knex": "^3.1.0", "pg": "^8.13.1", + "prisma": "^6.0.0", "ts-node": "^10.9.2" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 21a90d4..dc63f97 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,15 +8,24 @@ importers: .: dependencies: + '@fastify/multipart': + specifier: ^9.0.1 + version: 9.0.1 + '@prisma/client': + specifier: 6.0.0 + version: 6.0.0(prisma@6.0.0) + dotenv: + specifier: ^16.4.5 + version: 16.4.5 fastify: specifier: ^5.1.0 version: 5.1.0 - knex: - specifier: ^3.1.0 - version: 3.1.0(pg@8.13.1) pg: specifier: ^8.13.1 version: 8.13.1 + prisma: + specifier: ^6.0.0 + version: 6.0.0 ts-node: specifier: ^10.9.2 version: 10.9.2(@types/node@22.10.1)(typescript@5.7.2) @@ -30,6 +39,12 @@ packages: '@fastify/ajv-compiler@4.0.1': resolution: {integrity: sha512-DxrBdgsjNLP0YM6W5Hd6/Fmj43S8zMKiFJYgi+Ri3htTGAowPVG/tG1wpnWLMjufEnehRivUCKZ1pLDIoZdTuw==} + '@fastify/busboy@3.0.0': + resolution: {integrity: sha512-83rnH2nCvclWaPQQKvkJ2pdOjG4TZyEVuFDnlOF6KP08lDaaceVyw/W63mDuafQT+MKHCvXIPpE5uYWeM0rT4w==} + + '@fastify/deepmerge@2.0.0': + resolution: {integrity: sha512-fsaybTGDyQ5KpPsplQqb9yKdCf2x/pbNpMNk8Tvp3rRz7lVcupKysH4b2ELMN2P4Hak1+UqTYdTj/u4FNV2p0g==} + '@fastify/error@4.0.0': resolution: {integrity: sha512-OO/SA8As24JtT1usTUTKgGH7uLvhfwZPwlptRi2Dp5P4KKmJI3gvsZ8MIHnNwDs4sLf/aai5LzTyl66xr7qMxA==} @@ -39,6 +54,9 @@ packages: '@fastify/merge-json-schemas@0.1.1': resolution: {integrity: sha512-fERDVz7topgNjtXsJTTW1JKLy0rhuLRcquYqNR9rF7OcVpCa2OVW49ZPDIhaRRCaUuvVxI+N416xUoF76HNSXA==} + '@fastify/multipart@9.0.1': + resolution: {integrity: sha512-vt2gOCw/O4EwpN4KlLVJxth4iQlDf7T5ggw2Db2C+UbO2WJBG7y0jEBvu/HT6JIW/lBYaqrrUy9MmTpCKgXEpw==} + '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} @@ -49,6 +67,30 @@ packages: '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + '@prisma/client@6.0.0': + resolution: {integrity: sha512-tOBhG35ozqZ/5Y6B0TNOa6cwULUW8ijXqBXcgb12bfozqf6eGMyGs+jphywCsj6uojv5lAZZnxVSoLMVebIP+g==} + engines: {node: '>=18.18'} + peerDependencies: + prisma: '*' + peerDependenciesMeta: + prisma: + optional: true + + '@prisma/debug@6.0.0': + resolution: {integrity: sha512-eUjoNThlDXdyJ1iQ2d7U6aTVwm59EwvODb5zFVNJEokNoSiQmiYWNzZIwZyDmZ+j51j42/0iTaHIJ4/aZPKFRg==} + + '@prisma/engines-version@5.23.0-27.5dbef10bdbfb579e07d35cc85fb1518d357cb99e': + resolution: {integrity: sha512-JmIds0Q2/vsOmnuTJYxY4LE+sajqjYKhLtdOT6y4imojqv5d/aeVEfbBGC74t8Be1uSp0OP8lxIj2OqoKbLsfQ==} + + '@prisma/engines@6.0.0': + resolution: {integrity: sha512-ZZCVP3q22ifN6Ex6C8RIcTDBlRtMJS2H1ljV0knCiWNGArvvkEbE88W3uDdq/l4+UvyvHpGzdf9ZsCWSQR7ZQQ==} + + '@prisma/fetch-engine@6.0.0': + resolution: {integrity: sha512-j2m+iO5RDPRI7SUc7sHo8wX7SA4iTkJ+18Sxch8KinQM46YiCQD1iXKN6qU79C1Fliw5Bw/qDyTHaTsa3JMerA==} + + '@prisma/get-platform@6.0.0': + resolution: {integrity: sha512-PS6nYyIm9g8C03E4y7LknOfdCw/t2KyEJxntMPQHQZCOUgOpF82Ma60mdlOD08w90I3fjLiZZ0+MadenR3naDQ==} + '@tsconfig/node10@1.0.11': resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} @@ -97,13 +139,6 @@ packages: avvio@9.1.0: resolution: {integrity: sha512-fYASnYi600CsH/j9EQov7lECAniYiBFiiAtBNuZYLA2leLe9qOvZzqYHFjtIj6gD2VMoMLP14834LFWvr4IfDw==} - colorette@2.0.19: - resolution: {integrity: sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==} - - commander@10.0.1: - resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} - engines: {node: '>=14'} - cookie@1.0.2: resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} engines: {node: '>=18'} @@ -111,26 +146,13 @@ packages: create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - debug@4.3.4: - resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - diff@4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} - escalade@3.2.0: - resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} - engines: {node: '>=6'} - - esm@3.2.25: - resolution: {integrity: sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==} - engines: {node: '>=6'} + dotenv@16.4.5: + resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} + engines: {node: '>=12'} fast-decode-uri-component@1.0.1: resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} @@ -154,6 +176,9 @@ packages: fast-uri@3.0.3: resolution: {integrity: sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==} + fastify-plugin@5.0.1: + resolution: {integrity: sha512-HCxs+YnRaWzCl+cWRYFnHmeRFyR5GVnJTAaCJQiYzQSDwK9MgJdyAsuL3nh0EWRCYMgQ5MeziymvmAhUHYHDUQ==} + fastify@5.1.0: resolution: {integrity: sha512-0SdUC5AoiSgMSc2Vxwv3WyKzyGMDJRAW/PgNsK1kZrnkO6MeqUIW9ovVg9F2UGIqtIcclYMyeJa4rK6OZc7Jxg==} @@ -168,91 +193,34 @@ packages: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} - function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - - get-package-type@0.1.0: - resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} - engines: {node: '>=8.0.0'} - - getopts@2.3.0: - resolution: {integrity: sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA==} - - hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} - engines: {node: '>= 0.4'} - - interpret@2.2.0: - resolution: {integrity: sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==} - engines: {node: '>= 0.10'} + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] ipaddr.js@1.9.1: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} - is-core-module@2.15.1: - resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==} - engines: {node: '>= 0.4'} - json-schema-ref-resolver@1.0.1: resolution: {integrity: sha512-EJAj1pgHc1hxF6vo2Z3s69fMjO1INq6eGHXZ8Z6wCQeldCuwxGK9Sxf4/cScGn3FZubCVUehfWtcDM/PLteCQw==} json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - knex@3.1.0: - resolution: {integrity: sha512-GLoII6hR0c4ti243gMs5/1Rb3B+AjwMOfjYm97pu0FOQa7JH56hgBxYf5WK2525ceSbBY1cjeZ9yk99GPMB6Kw==} - engines: {node: '>=16'} - hasBin: true - peerDependencies: - better-sqlite3: '*' - mysql: '*' - mysql2: '*' - pg: '*' - pg-native: '*' - sqlite3: '*' - tedious: '*' - peerDependenciesMeta: - better-sqlite3: - optional: true - mysql: - optional: true - mysql2: - optional: true - pg: - optional: true - pg-native: - optional: true - sqlite3: - optional: true - tedious: - optional: true - light-my-request@6.3.0: resolution: {integrity: sha512-bWTAPJmeWQH5suJNYwG0f5cs0p6ho9e6f1Ppoxv5qMosY+s9Ir2+ZLvvHcgA7VTDop4zl/NCHhOVVqU+kd++Ow==} - lodash@4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} - ms@2.1.2: - resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - on-exit-leak-free@2.1.2: resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} engines: {node: '>=14.0.0'} - path-parse@1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - pg-cloudflare@1.1.1: resolution: {integrity: sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==} - pg-connection-string@2.6.2: - resolution: {integrity: sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==} - pg-connection-string@2.7.0: resolution: {integrity: sha512-PI2W9mv53rXJQEOb8xNR8lH7Hr+EKa6oJa38zsK0S/ky2er16ios1wLKhZyxzD7jUReiWokc9WK5nxSnC7W1TA==} @@ -310,6 +278,11 @@ packages: resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} engines: {node: '>=0.10.0'} + prisma@6.0.0: + resolution: {integrity: sha512-RX7KtbW7IoEByf7MR32JK1PkVYLVYFqeODTtiIX3cqekq1aKdsF3Eud4zp2sUShMLjvdb5Jow0LbUjRq5LVxPw==} + engines: {node: '>=18.18'} + hasBin: true + process-warning@4.0.0: resolution: {integrity: sha512-/MyYDxttz7DfGMMHiysAsFE4qF+pQYAA8ziO/3NcRVrQ5fSk+Mns4QZA/oRPFzvcqNoVJXQNWNAsdwBXLUkQKw==} @@ -324,22 +297,10 @@ packages: resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} engines: {node: '>= 12.13.0'} - rechoir@0.8.0: - resolution: {integrity: sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==} - engines: {node: '>= 10.13.0'} - require-from-string@2.0.2: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} - resolve-from@5.0.0: - resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} - engines: {node: '>=8'} - - resolve@1.22.8: - resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} - hasBin: true - ret@0.5.0: resolution: {integrity: sha512-I1XxrZSQ+oErkRR4jYbAyEEu2I0avBvvMM5JN+6EBprOGRCs63ENqZ3vjavq8fBw2+62G5LF5XelKwuJpcvcxw==} engines: {node: '>=10'} @@ -361,6 +322,9 @@ packages: secure-json-parse@2.7.0: resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} + secure-json-parse@3.0.1: + resolution: {integrity: sha512-9QR7G96th4QJ2+dJwvZB+JoXyt8PN+DbEjOr6kL2/JU4KH8Eb2sFdU+gt8EDdzWDWoWH0uocDdfCoFzdVSixUA==} + semver@7.6.3: resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} engines: {node: '>=10'} @@ -376,21 +340,9 @@ packages: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} engines: {node: '>= 10.x'} - supports-preserve-symlinks-flag@1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} - - tarn@3.0.2: - resolution: {integrity: sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==} - engines: {node: '>=8.0.0'} - thread-stream@3.1.0: resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==} - tildify@2.0.0: - resolution: {integrity: sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw==} - engines: {node: '>=8'} - toad-cache@3.7.0: resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==} engines: {node: '>=12'} @@ -440,6 +392,10 @@ snapshots: ajv-formats: 3.0.1(ajv@8.17.1) fast-uri: 3.0.3 + '@fastify/busboy@3.0.0': {} + + '@fastify/deepmerge@2.0.0': {} + '@fastify/error@4.0.0': {} '@fastify/fast-json-stringify-compiler@5.0.1': @@ -450,6 +406,14 @@ snapshots: dependencies: fast-deep-equal: 3.1.3 + '@fastify/multipart@9.0.1': + dependencies: + '@fastify/busboy': 3.0.0 + '@fastify/deepmerge': 2.0.0 + '@fastify/error': 4.0.0 + fastify-plugin: 5.0.1 + secure-json-parse: 3.0.1 + '@jridgewell/resolve-uri@3.1.2': {} '@jridgewell/sourcemap-codec@1.5.0': {} @@ -459,6 +423,31 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 + '@prisma/client@6.0.0(prisma@6.0.0)': + optionalDependencies: + prisma: 6.0.0 + + '@prisma/debug@6.0.0': {} + + '@prisma/engines-version@5.23.0-27.5dbef10bdbfb579e07d35cc85fb1518d357cb99e': {} + + '@prisma/engines@6.0.0': + dependencies: + '@prisma/debug': 6.0.0 + '@prisma/engines-version': 5.23.0-27.5dbef10bdbfb579e07d35cc85fb1518d357cb99e + '@prisma/fetch-engine': 6.0.0 + '@prisma/get-platform': 6.0.0 + + '@prisma/fetch-engine@6.0.0': + dependencies: + '@prisma/debug': 6.0.0 + '@prisma/engines-version': 5.23.0-27.5dbef10bdbfb579e07d35cc85fb1518d357cb99e + '@prisma/get-platform': 6.0.0 + + '@prisma/get-platform@6.0.0': + dependencies: + '@prisma/debug': 6.0.0 + '@tsconfig/node10@1.0.11': {} '@tsconfig/node12@1.0.11': {} @@ -499,23 +488,13 @@ snapshots: '@fastify/error': 4.0.0 fastq: 1.17.1 - colorette@2.0.19: {} - - commander@10.0.1: {} - cookie@1.0.2: {} create-require@1.1.1: {} - debug@4.3.4: - dependencies: - ms: 2.1.2 - diff@4.0.2: {} - escalade@3.2.0: {} - - esm@3.2.25: {} + dotenv@16.4.5: {} fast-decode-uri-component@1.0.1: {} @@ -541,6 +520,8 @@ snapshots: fast-uri@3.0.3: {} + fastify-plugin@5.0.1: {} + fastify@5.1.0: dependencies: '@fastify/ajv-compiler': 4.0.1 @@ -571,72 +552,30 @@ snapshots: forwarded@0.2.0: {} - function-bind@1.1.2: {} - - get-package-type@0.1.0: {} - - getopts@2.3.0: {} - - hasown@2.0.2: - dependencies: - function-bind: 1.1.2 - - interpret@2.2.0: {} + fsevents@2.3.3: + optional: true ipaddr.js@1.9.1: {} - is-core-module@2.15.1: - dependencies: - hasown: 2.0.2 - json-schema-ref-resolver@1.0.1: dependencies: fast-deep-equal: 3.1.3 json-schema-traverse@1.0.0: {} - knex@3.1.0(pg@8.13.1): - dependencies: - colorette: 2.0.19 - commander: 10.0.1 - debug: 4.3.4 - escalade: 3.2.0 - esm: 3.2.25 - get-package-type: 0.1.0 - getopts: 2.3.0 - interpret: 2.2.0 - lodash: 4.17.21 - pg-connection-string: 2.6.2 - rechoir: 0.8.0 - resolve-from: 5.0.0 - tarn: 3.0.2 - tildify: 2.0.0 - optionalDependencies: - pg: 8.13.1 - transitivePeerDependencies: - - supports-color - light-my-request@6.3.0: dependencies: cookie: 1.0.2 process-warning: 4.0.0 set-cookie-parser: 2.7.1 - lodash@4.17.21: {} - make-error@1.3.6: {} - ms@2.1.2: {} - on-exit-leak-free@2.1.2: {} - path-parse@1.0.7: {} - pg-cloudflare@1.1.1: optional: true - pg-connection-string@2.6.2: {} - pg-connection-string@2.7.0: {} pg-int8@1.0.1: {} @@ -699,6 +638,12 @@ snapshots: dependencies: xtend: 4.0.2 + prisma@6.0.0: + dependencies: + '@prisma/engines': 6.0.0 + optionalDependencies: + fsevents: 2.3.3 + process-warning@4.0.0: {} proxy-addr@2.0.7: @@ -710,20 +655,8 @@ snapshots: real-require@0.2.0: {} - rechoir@0.8.0: - dependencies: - resolve: 1.22.8 - require-from-string@2.0.2: {} - resolve-from@5.0.0: {} - - resolve@1.22.8: - dependencies: - is-core-module: 2.15.1 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - ret@0.5.0: {} reusify@1.0.4: {} @@ -738,6 +671,8 @@ snapshots: secure-json-parse@2.7.0: {} + secure-json-parse@3.0.1: {} + semver@7.6.3: {} set-cookie-parser@2.7.1: {} @@ -748,16 +683,10 @@ snapshots: split2@4.2.0: {} - supports-preserve-symlinks-flag@1.0.0: {} - - tarn@3.0.2: {} - thread-stream@3.1.0: dependencies: real-require: 0.2.0 - tildify@2.0.0: {} - toad-cache@3.7.0: {} ts-node@10.9.2(@types/node@22.10.1)(typescript@5.7.2): diff --git a/prisma/migrations/20241201160113_init/migration.sql b/prisma/migrations/20241201160113_init/migration.sql new file mode 100644 index 0000000..93fbe89 --- /dev/null +++ b/prisma/migrations/20241201160113_init/migration.sql @@ -0,0 +1,15 @@ +-- CreateTable +CREATE TABLE "User" ( + "id" TEXT NOT NULL, + "discordToken" TEXT NOT NULL, + + CONSTRAINT "User_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "ImagePost" ( + "id" UUID NOT NULL, + "fileName" TEXT NOT NULL, + + CONSTRAINT "ImagePost_pkey" PRIMARY KEY ("id") +); diff --git a/prisma/migrations/20241201160930_test/migration.sql b/prisma/migrations/20241201160930_test/migration.sql new file mode 100644 index 0000000..a2713bc --- /dev/null +++ b/prisma/migrations/20241201160930_test/migration.sql @@ -0,0 +1,11 @@ +/* + Warnings: + + - Added the required column `userId` to the `ImagePost` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "ImagePost" ADD COLUMN "userId" TEXT NOT NULL; + +-- AddForeignKey +ALTER TABLE "ImagePost" ADD CONSTRAINT "ImagePost_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/migrations/20241201163616_asdkjlfhsdf/migration.sql b/prisma/migrations/20241201163616_asdkjlfhsdf/migration.sql new file mode 100644 index 0000000..1b4edb5 --- /dev/null +++ b/prisma/migrations/20241201163616_asdkjlfhsdf/migration.sql @@ -0,0 +1,23 @@ +/* + Warnings: + + - You are about to drop the `ImagePost` table. If the table is not empty, all the data it contains will be lost. + +*/ +-- DropForeignKey +ALTER TABLE "ImagePost" DROP CONSTRAINT "ImagePost_userId_fkey"; + +-- DropTable +DROP TABLE "ImagePost"; + +-- CreateTable +CREATE TABLE "FilePost" ( + "id" UUID NOT NULL, + "fileName" TEXT NOT NULL, + "userId" TEXT NOT NULL, + + CONSTRAINT "FilePost_pkey" PRIMARY KEY ("id") +); + +-- AddForeignKey +ALTER TABLE "FilePost" ADD CONSTRAINT "FilePost_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/migrations/20241201180810_yeah/migration.sql b/prisma/migrations/20241201180810_yeah/migration.sql new file mode 100644 index 0000000..96ced19 --- /dev/null +++ b/prisma/migrations/20241201180810_yeah/migration.sql @@ -0,0 +1,6 @@ +-- CreateTable +CREATE TABLE "WhitelistedUsers" ( + "discordId" TEXT NOT NULL, + + CONSTRAINT "WhitelistedUsers_pkey" PRIMARY KEY ("discordId") +); diff --git a/prisma/migrations/migration_lock.toml b/prisma/migrations/migration_lock.toml new file mode 100644 index 0000000..fbffa92 --- /dev/null +++ b/prisma/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (i.e. Git) +provider = "postgresql" \ No newline at end of file diff --git a/prisma/schema.prisma b/prisma/schema.prisma new file mode 100644 index 0000000..d2e811b --- /dev/null +++ b/prisma/schema.prisma @@ -0,0 +1,31 @@ +// This is your Prisma schema file, +// learn more about it in the docs: https://pris.ly/d/prisma-schema + +// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions? +// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init + +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +model WhitelistedUsers { + discordId String @id +} + +model User { + id String @id + discordToken String + imagePosts FilePost[] +} + +model FilePost { + id String @id @default(uuid()) @db.Uuid + fileName String + userId String + user User @relation(fields: [userId], references: [id]) +} diff --git a/src/authHelper.ts b/src/authHelper.ts new file mode 100644 index 0000000..f02f1d3 --- /dev/null +++ b/src/authHelper.ts @@ -0,0 +1,29 @@ +import {DiscordUser} from "./types.ts"; +import {FastifyRequest} from "fastify"; + +export function getAdmins(): string[] { + return process.env.ADMINS.split(":") +} + +export async function isTokenValid(token: string):Promise { + return fetch("https://discord.com/api/users/@me", { + headers: new Headers({ + "Authorization": `Bearer ${token}` + }) + }).then(res => { + return !res.ok; + }).catch(() => { + return false; + }); +} + +export async function isAdmin(token: string): Promise { + const response = await fetch("https://discord.com/api/users/@me", { + headers: new Headers({ + "Authorization": `Bearer ${token}` + }) + }); + + const user: DiscordUser = await response.json(); + return getAdmins().includes(user.id); +} \ No newline at end of file diff --git a/src/helper.ts b/src/helper.ts deleted file mode 100644 index 6d79bb9..0000000 --- a/src/helper.ts +++ /dev/null @@ -1,16 +0,0 @@ -export async function getAccountInfoBasedOnCode(code: string, clientId: string, clientSecret: string) { - await fetch('https://discord.com/api/oauth2/token', { - method: 'POST', - body: new URLSearchParams({ - client_id: clientId, - client_secret: clientSecret, - code, - grant_type: 'authorization_code', - redirect_uri: `http://localhost:${3000}`, - scope: 'identify', - }).toString(), - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - }, - }).then(res => res.json().then(console.log)) -} \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index e903401..a35ac9f 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,41 +1,88 @@ import Fastify from 'fastify' -import {getAccountInfoBasedOnCode} from "./helper.ts"; +import { PrismaClient } from '@prisma/client' +import fastifyMultipart from "@fastify/multipart"; +import fs from 'fs' +import path from 'path' +import dotenv from 'dotenv'; +import { fileURLToPath } from 'url'; +import {isAdmin, isTokenValid} from "./authHelper.ts"; +import {checkadmin} from "./middleware.ts"; -import Knex from 'knex'; +dotenv.config(); -const knex = Knex({ - client: 'pg', - connection: { - host: '127.0.0.1', - user: 'postgres', - password: 'postgres', - database: 'ether' - }, - migrations: { - directory: './migrations' - }, - seeds: { - directory: './seeds' +if (!process.env.ADMINS) { + console.error("no admin account found"); + process.exit(1); +} +export const __filename = fileURLToPath(import.meta.url); +export const __dirname = path.dirname(__filename); + +const prisma = new PrismaClient() +const fastify = Fastify({ logger: true }) + +// Register fastify-multipart for file uploads +fastify.register(fastifyMultipart) + +// Upload directory +const uploadDir = path.join(__dirname, 'uploads') +if (!fs.existsSync(uploadDir)) { + fs.mkdirSync(uploadDir) +} + +fastify.post('/api/admin/addwhitelist', async (request, reply) => { + const test = await checkadmin(request) + if (!test) return reply.status(401).send('Not Authorized'); + const {id} = request.query as { id?: string }; + if (!id) { + reply.status(400).send('Bad Request'); + } + await prisma.whitelistedUsers.create({ + data: { + discordId: id, + } + }).then(() => { + reply.status(200).send('User added successfully'); + }) +}) + +fastify.get('/', async (request, reply) => { + + return reply.status(200).send('ok') +}) + +fastify.post('/api/upload', async (request, reply) => { + const data = await request.file() + const fileName = `${Date.now()}-${data.filename}` + const filePath = path.join(uploadDir, fileName) + + await new Promise((resolve, reject) => { + const fileStream = fs.createWriteStream(filePath) + data.file.pipe(fileStream) + fileStream.on('finish', resolve) + fileStream.on('error', reject) + }) + + try { + // const newImagePost = await prisma.filePost.create({ + // data: { + // fileName, + // user: { + // connect: {id: bew.id} + // } + // } + // }) + // reply.code(201).send(newImagePost) + } catch (error) { + fastify.log.error(error) + reply.code(500).send({ error: 'Error uploading file or saving record' }) } }) -const fastify = Fastify({ - logger: true +fastify.listen({ port: 3000 }, (err, address) => { + if (err) { + fastify.log.error(err) + process.exit(1) + } + fastify.log.info(`Server running at ${address}`) }) - -// Declare a route -fastify.get('/', async (request, reply) => { - getAccountInfoBasedOnCode("0FvE4z2uFfpil8PHV9bqrQu1Ioo70N", "1312585635361718272", "aiZqJoBCEqAOmTVsjnEGmr1qoVxXBYTR") -}) - -fastify.get('/balls', async (request, reply) => { - reply.redirect("https://google.com") -}) - -try { - await fastify.listen({port: 3000}) -} catch (err) { - fastify.log.error(err) - process.exit(1) -} \ No newline at end of file diff --git a/src/middleware.ts b/src/middleware.ts new file mode 100644 index 0000000..8871761 --- /dev/null +++ b/src/middleware.ts @@ -0,0 +1,10 @@ +import {FastifyRequest} from "fastify"; +import {isAdmin} from "./authHelper.ts"; + +export async function checkadmin(request: FastifyRequest):Promise { + const Authheader = request.headers.authorization + if (!Authheader) { + return false + } + return isAdmin(Authheader) +} \ No newline at end of file diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..02c9e92 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,18 @@ +export interface DiscordUser { + id: string; + username: string; + avatar: string; + discriminator: string; + public_flags: number; + flags: number; + banner: string | null; + accent_color: string | null; + global_name: string; + avatar_decoration_data: any | null; + banner_color: string | null; + clan: any | null; + primary_guild: any | null; + mfa_enabled: boolean; + locale: string; + premium_type: number; +}