diff --git a/astro.config.mjs b/astro.config.mjs index 882e651..17786e1 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -1,4 +1,15 @@ import { defineConfig } from 'astro/config'; +import node from "@astrojs/node"; + // https://astro.build/config -export default defineConfig({}); +export default defineConfig({ + server: { + port: 8900, + host: true + }, + output: 'server', + adapter: node({ + mode: "standalone" + }) +}); \ No newline at end of file diff --git a/package.json b/package.json index 0a653c1..1f29921 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,18 @@ "astro": "astro" }, "dependencies": { - "astro": "^2.4.1" + "@astrojs/node": "^5.1.2", + "@noble/hashes": "^1.3.0", + "@popperjs/core": "^2.11.7", + "@prisma/client": "4.13.0", + "@types/bootstrap": "^5.2.6", + "astro": "^2.4.1", + "astro-bootstrap": "^0.5.13", + "bootstrap": "^5.2.3", + "sharp": "^0.32.1" + }, + "devDependencies": { + "@types/node": "^20.1.1", + "prisma": "^4.13.0" } } \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 69b0862..fca6f67 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,9 +1,41 @@ lockfileVersion: '6.0' dependencies: + '@astrojs/node': + specifier: ^5.1.2 + version: 5.1.2(astro@2.4.1) + '@noble/hashes': + specifier: ^1.3.0 + version: 1.3.0 + '@popperjs/core': + specifier: ^2.11.7 + version: 2.11.7 + '@prisma/client': + specifier: 4.13.0 + version: 4.13.0(prisma@4.13.0) + '@types/bootstrap': + specifier: ^5.2.6 + version: 5.2.6 astro: specifier: ^2.4.1 - version: 2.4.1 + version: 2.4.1(@types/node@20.1.1)(sharp@0.32.1) + astro-bootstrap: + specifier: ^0.5.13 + version: 0.5.13(astro@2.4.1)(bootstrap@5.2.3) + bootstrap: + specifier: ^5.2.3 + version: 5.2.3(@popperjs/core@2.11.7) + sharp: + specifier: ^0.32.1 + version: 0.32.1 + +devDependencies: + '@types/node': + specifier: ^20.1.1 + version: 20.1.1 + prisma: + specifier: ^4.13.0 + version: 4.13.0 packages: @@ -44,7 +76,7 @@ packages: astro: ^2.4.0 dependencies: '@astrojs/prism': 2.1.1 - astro: 2.4.1 + astro: 2.4.1(@types/node@20.1.1)(sharp@0.32.1) github-slugger: 1.5.0 import-meta-resolve: 2.2.2 rehype-raw: 6.1.1 @@ -61,6 +93,19 @@ packages: - supports-color dev: false + /@astrojs/node@5.1.2(astro@2.4.1): + resolution: {integrity: sha512-tDApFnU99O3KQXaRUmfwbkdxqgJhNvPDWou4CVBpybVm3xruigMfz5c+H9P2f4vRo0+sYTzof1L3Hzx9Rh9sQA==} + peerDependencies: + astro: ^2.3.3 + dependencies: + '@astrojs/webapi': 2.1.1 + astro: 2.4.1(@types/node@20.1.1)(sharp@0.32.1) + send: 0.18.0 + server-destroy: 1.0.1 + transitivePeerDependencies: + - supports-color + dev: false + /@astrojs/prism@2.1.1: resolution: {integrity: sha512-Gnwnlb1lGJzCQEg89r4/WqgfCGPNFC7Kuh2D/k289Cbdi/2PD7Lrdstz86y1itDvcb2ijiRqjqWnJ5rsfu/QOA==} engines: {node: '>=16.12.0'} @@ -573,6 +618,10 @@ packages: resolution: {integrity: sha512-4/RWEeXDO6bocPONheFe6gX/oQdP/bEpv0oL4HqjPP5DCenBSt0mHgahppY49N0CpsaqffdwPq+TlX9CYOq2Dw==} dev: false + /@noble/hashes@1.3.0: + resolution: {integrity: sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg==} + dev: false + /@nodelib/fs.scandir@2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -606,6 +655,32 @@ packages: tslib: 2.5.0 dev: false + /@popperjs/core@2.11.7: + resolution: {integrity: sha512-Cr4OjIkipTtcXKjAsm8agyleBuDHvxzeBoa1v543lbv1YaIwQjESsVcmjiWiPEbC1FIeHOG/Op9kdCmAmiS3Kw==} + dev: false + + /@prisma/client@4.13.0(prisma@4.13.0): + resolution: {integrity: sha512-YaiiICcRB2hatxsbnfB66uWXjcRw3jsZdlAVxmx0cFcTc/Ad/sKdHCcWSnqyDX47vAewkjRFwiLwrOUjswVvmA==} + engines: {node: '>=14.17'} + requiresBuild: true + peerDependencies: + prisma: '*' + peerDependenciesMeta: + prisma: + optional: true + dependencies: + '@prisma/engines-version': 4.13.0-50.1e7af066ee9cb95cf3a403c78d9aab3e6b04f37a + prisma: 4.13.0 + dev: false + + /@prisma/engines-version@4.13.0-50.1e7af066ee9cb95cf3a403c78d9aab3e6b04f37a: + resolution: {integrity: sha512-fsQlbkhPJf08JOzKoyoD9atdUijuGBekwoOPZC3YOygXEml1MTtgXVpnUNchQlRSY82OQ6pSGQ9PxUe4arcSLQ==} + dev: false + + /@prisma/engines@4.13.0: + resolution: {integrity: sha512-HrniowHRZXHuGT9XRgoXEaP2gJLXM5RMoItaY2PkjvuZ+iHc0Zjbm/302MB8YsPdWozAPHHn+jpFEcEn71OgPw==} + requiresBuild: true + /@types/babel__core@7.20.0: resolution: {integrity: sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==} dependencies: @@ -635,6 +710,12 @@ packages: '@babel/types': 7.21.5 dev: false + /@types/bootstrap@5.2.6: + resolution: {integrity: sha512-BlAc3YATdasbHoxMoBWODrSF6qwQO/E9X8wVxCCSa6rWjnaZfpkr2N6pUMCY6jj2+wf0muUtLySbvU9etX6YqA==} + dependencies: + '@popperjs/core': 2.11.7 + dev: false + /@types/debug@4.1.7: resolution: {integrity: sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==} dependencies: @@ -667,6 +748,9 @@ packages: '@types/unist': 2.0.6 dev: false + /@types/node@20.1.1: + resolution: {integrity: sha512-uKBEevTNb+l6/aCQaKVnUModfEMjAl98lw2Si9P5y4hLu9tm6AlX2ZIoXZX6Wh9lJueYPrGPKk5WMCNHg/u6/A==} + /@types/parse5@6.0.3: resolution: {integrity: sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g==} dev: false @@ -760,7 +844,23 @@ packages: resolution: {integrity: sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==} dev: false - /astro@2.4.1: + /astro-bootstrap@0.5.13(astro@2.4.1)(bootstrap@5.2.3): + resolution: {integrity: sha512-XbixeG+lu76Wy2rB8ae/wtRE5Kf90CID3usUO7HTc/h/RS4UxWk6meyWTIB083z9jCBsRbZN1G2FsmGkLoPd+Q==} + peerDependencies: + astro: '>=1.x' + bootstrap: 5.x + dependencies: + '@types/bootstrap': 5.2.6 + astro: 2.4.1(@types/node@20.1.1)(sharp@0.32.1) + bootstrap: 5.2.3(@popperjs/core@2.11.7) + clsx: 1.2.1 + dompurify: 2.4.5 + marked: 4.3.0 + nanoid: 4.0.2 + title-case: 3.0.3 + dev: false + + /astro@2.4.1(@types/node@20.1.1)(sharp@0.32.1): resolution: {integrity: sha512-qNYXxjtJm0+FHr+MtHXhV/WEMkoulAoCsvtdyJrQiuk9raodPo2xNHgP1WPZUtmFlPN2ezZ2XGDUK1zmFZgRLg==} engines: {node: '>=16.12.0', npm: '>=6.14.0'} hasBin: true @@ -810,6 +910,7 @@ packages: rehype: 12.0.1 semver: 7.5.0 server-destroy: 1.0.1 + sharp: 0.32.1 shiki: 0.14.2 slash: 4.0.0 string-width: 5.1.2 @@ -819,7 +920,7 @@ packages: typescript: 5.0.4 unist-util-visit: 4.1.2 vfile: 5.3.7 - vite: 4.3.5 + vite: 4.3.5(@types/node@20.1.1) vitefu: 0.2.4(vite@4.3.5) yargs-parser: 21.1.1 zod: 3.21.4 @@ -851,6 +952,14 @@ packages: engines: {node: '>=8'} dev: false + /bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + dev: false + /bl@5.1.0: resolution: {integrity: sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==} dependencies: @@ -859,6 +968,14 @@ packages: readable-stream: 3.6.2 dev: false + /bootstrap@5.2.3(@popperjs/core@2.11.7): + resolution: {integrity: sha512-cEKPM+fwb3cT8NzQZYEu4HilJ3anCrWqh3CHAok1p9jXqMPsPTBhU25fBckEJHJ/p+tTxTFTsFQGM+gaHpi3QQ==} + peerDependencies: + '@popperjs/core': ^2.11.6 + dependencies: + '@popperjs/core': 2.11.7 + dev: false + /boxen@6.2.1: resolution: {integrity: sha512-H4PEsJXfFI/Pt8sjDWbHlQPx4zL/bvSQjcilJmaulGt5mLDorHOHpmdXAJcBcmru7PhYSp/cDMWRko4ZUMFkSw==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -898,6 +1015,13 @@ packages: update-browserslist-db: 1.0.11(browserslist@4.21.5) dev: false + /buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + dev: false + /buffer@6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} dependencies: @@ -981,6 +1105,10 @@ packages: fsevents: 2.3.2 dev: false + /chownr@1.1.4: + resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + dev: false + /ci-info@3.8.0: resolution: {integrity: sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==} engines: {node: '>=8'} @@ -1008,6 +1136,11 @@ packages: engines: {node: '>=0.8'} dev: false + /clsx@1.2.1: + resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==} + engines: {node: '>=6'} + dev: false + /color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} dependencies: @@ -1029,6 +1162,21 @@ packages: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} dev: false + /color-string@1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.2 + dev: false + + /color@4.2.3: + resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} + engines: {node: '>=12.5.0'} + dependencies: + color-convert: 2.0.1 + color-string: 1.9.1 + dev: false + /comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} dev: false @@ -1055,6 +1203,17 @@ packages: which: 2.0.2 dev: false + /debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.0.0 + dev: false + /debug@4.3.4: resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} @@ -1073,6 +1232,18 @@ packages: character-entities: 2.0.2 dev: false + /decompress-response@6.0.0: + resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} + engines: {node: '>=10'} + dependencies: + mimic-response: 3.1.0 + dev: false + + /deep-extend@0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + dev: false + /deepmerge-ts@4.3.0: resolution: {integrity: sha512-if3ZYdkD2dClhnXR5reKtG98cwyaRT1NeugQoAPTTfsOpV9kqyeiBF9Qa5RHjemb3KzD5ulqygv6ED3t5j9eJw==} engines: {node: '>=12.4.0'} @@ -1107,11 +1278,26 @@ packages: engines: {node: '>=12'} dev: false + /depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + dev: false + /dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} dev: false + /destroy@1.2.0: + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + dev: false + + /detect-libc@2.0.1: + resolution: {integrity: sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==} + engines: {node: '>=8'} + dev: false + /devalue@4.3.0: resolution: {integrity: sha512-n94yQo4LI3w7erwf84mhRUkUJfhLoCZiLyoOZ/QFsDbcWNZePrLwbQpvZBUG2TNxwV3VjCKPxkiiQA6pe3TrTA==} dev: false @@ -1125,6 +1311,10 @@ packages: resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} dev: false + /dompurify@2.4.5: + resolution: {integrity: sha512-jggCCd+8Iqp4Tsz0nIvpcb22InKEBrGz5dw3EQJMs8HPJDsKbFIO3STYtAvCfDx26Muevn1MHVI0XxjgFfmiSA==} + dev: false + /dset@3.1.2: resolution: {integrity: sha512-g/M9sqy3oHe477Ar4voQxWtaPIFw1jTdKZuomOjhCcBx9nHUNn0pu6NopuFFrTh/TRZIKEj+76vLWFu9BNKk+Q==} engines: {node: '>=4'} @@ -1134,6 +1324,10 @@ packages: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} dev: false + /ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + dev: false + /electron-to-chromium@1.4.387: resolution: {integrity: sha512-tutLf+alr1/0YqJwKPdstVvDLmxmLb5xNyDLNS0RZmenHcEYk9qKfpKDCVZEKJ00JVbnayJm1MZAbYhYDFpcOw==} dev: false @@ -1153,6 +1347,17 @@ packages: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} dev: false + /encodeurl@1.0.2: + resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} + engines: {node: '>= 0.8'} + dev: false + + /end-of-stream@1.4.4: + resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + dependencies: + once: 1.4.0 + dev: false + /es-module-lexer@1.2.1: resolution: {integrity: sha512-9978wrXM50Y4rTMmW5kXIC09ZdXQZqkE4mxhwkd8VbzsGkXGPgV4zWuqQJgCEzYngdo2dYDa0l8xhX4fkSwJSg==} dev: false @@ -1192,6 +1397,10 @@ packages: engines: {node: '>=6'} dev: false + /escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + dev: false + /escape-string-regexp@1.0.5: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} @@ -1212,6 +1421,11 @@ packages: resolution: {integrity: sha512-s6ceX0NFiU/vKPiKvFdR83U1Zffu7upwZsGwpoqfg5rbbq1l50WQ5hCeIvM6E6oD4shUHCYMsiFPns4Jk0YfMQ==} dev: false + /etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + dev: false + /events@3.3.0: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} @@ -1262,6 +1476,11 @@ packages: strip-final-newline: 3.0.0 dev: false + /expand-template@2.0.3: + resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} + engines: {node: '>=6'} + dev: false + /extend-shallow@2.0.1: resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} engines: {node: '>=0.10.0'} @@ -1320,6 +1539,15 @@ packages: pkg-dir: 4.2.0 dev: false + /fresh@0.5.2: + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + engines: {node: '>= 0.6'} + dev: false + + /fs-constants@1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + dev: false + /fsevents@2.3.2: resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -1342,6 +1570,10 @@ packages: engines: {node: '>=10'} dev: false + /github-from-package@0.0.0: + resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} + dev: false + /github-slugger@1.5.0: resolution: {integrity: sha512-wIh+gKBI9Nshz2o46B0B3f5k/W+WI9ZAv6y5Dn5WJ5SK1t0TnDimB4WE5rmTD05ZAIn8HALCZVmCsvj0w0v0lw==} dev: false @@ -1482,6 +1714,17 @@ packages: resolution: {integrity: sha512-0quDb7s97CfemeJAnW9wC0hw78MtW7NU3hqtCD75g2vFlDLt36llsYD7uB7SUzojLMP24N5IatXf7ylGXiGG9A==} dev: false + /http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + dev: false + /human-signals@2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} @@ -1509,6 +1752,14 @@ packages: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} dev: false + /ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + dev: false + + /is-arrayish@0.3.2: + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + dev: false + /is-binary-path@2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} @@ -1717,6 +1968,12 @@ packages: resolution: {integrity: sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==} dev: false + /marked@4.3.0: + resolution: {integrity: sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==} + engines: {node: '>= 12'} + hasBin: true + dev: false + /mdast-util-definitions@5.1.2: resolution: {integrity: sha512-8SVPMuHqlPME/z3gqVwWY4zVXn8lqKv/pAhC57FuJ40ImXyBpmO5ukh98zB2v7Blql2FiHjHv9LVztSIqjY+MA==} dependencies: @@ -2113,6 +2370,12 @@ packages: picomatch: 2.3.1 dev: false + /mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + dev: false + /mime@3.0.0: resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} engines: {node: '>=10.0.0'} @@ -2129,27 +2392,69 @@ packages: engines: {node: '>=12'} dev: false + /mimic-response@3.1.0: + resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} + engines: {node: '>=10'} + dev: false + + /minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + dev: false + + /mkdirp-classic@0.5.3: + resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + dev: false + /mri@1.2.0: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} dev: false + /ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + dev: false + /ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} dev: false + /ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + dev: false + /nanoid@3.3.6: resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true dev: false + /nanoid@4.0.2: + resolution: {integrity: sha512-7ZtY5KTCNheRGfEFxnedV5zFiORN1+Y1N6zvPTnHQd8ENUvfaDBeuJDZb2bN/oXwXxu3qkTXDzy57W5vAmDTBw==} + engines: {node: ^14 || ^16 || >=18} + hasBin: true + dev: false + + /napi-build-utils@1.0.2: + resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==} + dev: false + /nlcst-to-string@3.1.1: resolution: {integrity: sha512-63mVyqaqt0cmn2VcI2aH6kxe1rLAmSROqHMA0i4qqg1tidkfExgpb0FGMikMCn86mw5dFtBtEANfmSSK7TjNHw==} dependencies: '@types/nlcst': 1.0.0 dev: false + /node-abi@3.40.0: + resolution: {integrity: sha512-zNy02qivjjRosswoYmPi8hIKJRr8MpQyeKT6qlcq/OnOgA3Rhoae+IYOqsM9V5+JnHWmxKnWOT2GxvtqdtOCXA==} + engines: {node: '>=10'} + dependencies: + semver: 7.5.0 + dev: false + + /node-addon-api@6.1.0: + resolution: {integrity: sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==} + dev: false + /node-releases@2.0.10: resolution: {integrity: sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==} dev: false @@ -2173,6 +2478,19 @@ packages: path-key: 4.0.0 dev: false + /on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + dependencies: + ee-first: 1.1.1 + dev: false + + /once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + dependencies: + wrappy: 1.0.2 + dev: false + /onetime@5.1.2: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} @@ -2310,6 +2628,25 @@ packages: source-map-js: 1.0.2 dev: false + /prebuild-install@7.1.1: + resolution: {integrity: sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==} + engines: {node: '>=10'} + hasBin: true + dependencies: + detect-libc: 2.0.1 + expand-template: 2.0.3 + github-from-package: 0.0.0 + minimist: 1.2.8 + mkdirp-classic: 0.5.3 + napi-build-utils: 1.0.2 + node-abi: 3.40.0 + pump: 3.0.0 + rc: 1.2.8 + simple-get: 4.0.1 + tar-fs: 2.1.1 + tunnel-agent: 0.6.0 + dev: false + /preferred-pm@3.0.3: resolution: {integrity: sha512-+wZgbxNES/KlJs9q40F/1sfOd/j7f1O9JaHcW5Dsn3aUUOZg3L2bjpVUcKV2jvtElYfoTuQiNeMfQJ4kwUAhCQ==} engines: {node: '>=10'} @@ -2336,6 +2673,14 @@ packages: hasBin: true dev: false + /prisma@4.13.0: + resolution: {integrity: sha512-L9mqjnSmvWIRCYJ9mQkwCtj4+JDYYTdhoyo8hlsHNDXaZLh/b4hR0IoKIBbTKxZuyHQzLopb/+0Rvb69uGV7uA==} + engines: {node: '>=14.17'} + hasBin: true + requiresBuild: true + dependencies: + '@prisma/engines': 4.13.0 + /prismjs@1.29.0: resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==} engines: {node: '>=6'} @@ -2353,10 +2698,32 @@ packages: resolution: {integrity: sha512-kma4U7AFCTwpqq5twzC1YVIDXSqg6qQK6JN0smOw8fgRy1OkMi0CYSzFmsy6dnqSenamAtj0CyXMUJ1Mf6oROg==} dev: false + /pump@3.0.0: + resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} + dependencies: + end-of-stream: 1.4.4 + once: 1.4.0 + dev: false + /queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} dev: false + /range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + dev: false + + /rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.8 + strip-json-comments: 2.0.1 + dev: false + /readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} @@ -2566,10 +2933,50 @@ packages: lru-cache: 6.0.0 dev: false + /send@0.18.0: + resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} + engines: {node: '>= 0.8.0'} + dependencies: + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 0.5.2 + http-errors: 2.0.0 + mime: 1.6.0 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.1 + transitivePeerDependencies: + - supports-color + dev: false + /server-destroy@1.0.1: resolution: {integrity: sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ==} dev: false + /setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + dev: false + + /sharp@0.32.1: + resolution: {integrity: sha512-kQTFtj7ldpUqSe8kDxoGLZc1rnMFU0AO2pqbX6pLy3b7Oj8ivJIdoKNwxHVQG2HN6XpHPJqCSM2nsma2gOXvOg==} + engines: {node: '>=14.15.0'} + requiresBuild: true + dependencies: + color: 4.2.3 + detect-libc: 2.0.1 + node-addon-api: 6.1.0 + prebuild-install: 7.1.1 + semver: 7.5.0 + simple-get: 4.0.1 + tar-fs: 2.1.1 + tunnel-agent: 0.6.0 + dev: false + /shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -2595,6 +3002,24 @@ packages: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} dev: false + /simple-concat@1.0.1: + resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} + dev: false + + /simple-get@4.0.1: + resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} + dependencies: + decompress-response: 6.0.0 + once: 1.4.0 + simple-concat: 1.0.1 + dev: false + + /simple-swizzle@0.2.2: + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + dependencies: + is-arrayish: 0.3.2 + dev: false + /sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} dev: false @@ -2617,6 +3042,11 @@ packages: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} dev: false + /statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + dev: false + /stdin-discarder@0.1.0: resolution: {integrity: sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -2699,6 +3129,11 @@ packages: engines: {node: '>=12'} dev: false + /strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + dev: false + /suf-log@2.5.3: resolution: {integrity: sha512-KvC8OPjzdNOe+xQ4XWJV2whQA0aM1kGVczMQ8+dStAO6KfEB140JEVQ9dE76ONZ0/Ylf67ni4tILPJB41U0eow==} dependencies: @@ -2738,6 +3173,32 @@ packages: tslib: 2.5.0 dev: false + /tar-fs@2.1.1: + resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==} + dependencies: + chownr: 1.1.4 + mkdirp-classic: 0.5.3 + pump: 3.0.0 + tar-stream: 2.2.0 + dev: false + + /tar-stream@2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + dependencies: + bl: 4.1.0 + end-of-stream: 1.4.4 + fs-constants: 1.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + dev: false + + /title-case@3.0.3: + resolution: {integrity: sha512-e1zGYRvbffpcHIrnuqT0Dh+gEJtDaxDSoG4JAIpq4oDFyooziLBIiYQv0GBT4FUAnUop5uZ1hiIAj7oAF6sOCA==} + dependencies: + tslib: 2.5.0 + dev: false + /titleize@3.0.0: resolution: {integrity: sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==} engines: {node: '>=12'} @@ -2755,6 +3216,11 @@ packages: is-number: 7.0.0 dev: false + /toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + dev: false + /trim-lines@3.0.1: resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} dev: false @@ -2778,6 +3244,12 @@ packages: resolution: {integrity: sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==} dev: false + /tunnel-agent@0.6.0: + resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} + dependencies: + safe-buffer: 5.2.1 + dev: false + /type-fest@0.13.1: resolution: {integrity: sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==} engines: {node: '>=10'} @@ -2921,7 +3393,7 @@ packages: vfile-message: 3.1.4 dev: false - /vite@4.3.5: + /vite@4.3.5(@types/node@20.1.1): resolution: {integrity: sha512-0gEnL9wiRFxgz40o/i/eTBwm+NEbpUeTWhzKrZDSdKm6nplj+z4lKz8ANDgildxHm47Vg8EUia0aicKbawUVVA==} engines: {node: ^14.18.0 || >=16.0.0} hasBin: true @@ -2946,6 +3418,7 @@ packages: terser: optional: true dependencies: + '@types/node': 20.1.1 esbuild: 0.17.18 postcss: 8.4.23 rollup: 3.21.5 @@ -2961,7 +3434,7 @@ packages: vite: optional: true dependencies: - vite: 4.3.5 + vite: 4.3.5(@types/node@20.1.1) dev: false /vscode-css-languageservice@6.2.5: @@ -3072,6 +3545,10 @@ packages: strip-ansi: 7.0.1 dev: false + /wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + dev: false + /yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} dev: false diff --git a/prisma/schema.prisma b/prisma/schema.prisma new file mode 100644 index 0000000..90c3cd8 --- /dev/null +++ b/prisma/schema.prisma @@ -0,0 +1,69 @@ +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +model news { + id Int @id(map: "pk_news") @default(autoincrement()) + title String + message String + is_alert Boolean @default(false) + created_at DateTime @default(now()) +} + +model study_item { + id Int @id(map: "pk_study_item") @default(autoincrement()) + title String + study_slot study_slot[] +} + +model study_slot { + id Int @id(map: "pk_study_slot") @default(autoincrement()) + study_item_id Int? + study_item study_item? @relation(fields: [study_item_id], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "fk_study_slot_study_item") + timetable_timetable_slot_1Tostudy_slot timetable[] @relation("timetable_slot_1Tostudy_slot") + timetable_timetable_slot_2Tostudy_slot timetable[] @relation("timetable_slot_2Tostudy_slot") + timetable_timetable_slot_3Tostudy_slot timetable[] @relation("timetable_slot_3Tostudy_slot") + timetable_timetable_slot_4Tostudy_slot timetable[] @relation("timetable_slot_4Tostudy_slot") + timetable_timetable_slot_5Tostudy_slot timetable[] @relation("timetable_slot_5Tostudy_slot") + timetable_timetable_slot_6Tostudy_slot timetable[] @relation("timetable_slot_6Tostudy_slot") +} + +model timetable { + id Int @id(map: "pk_timetable") @default(autoincrement()) + slot_1 Int? + slot_2 Int? + slot_3 Int? + slot_4 Int? + slot_5 Int? + slot_6 Int? + day Int @db.SmallInt + teacher Int + study_slot_timetable_slot_1Tostudy_slot study_slot? @relation("timetable_slot_1Tostudy_slot", fields: [slot_1], references: [id], map: "fk_timetable_study_slot_1") + study_slot_timetable_slot_2Tostudy_slot study_slot? @relation("timetable_slot_2Tostudy_slot", fields: [slot_2], references: [id], map: "fk_timetable_study_slot_2") + study_slot_timetable_slot_3Tostudy_slot study_slot? @relation("timetable_slot_3Tostudy_slot", fields: [slot_3], references: [id], map: "fk_timetable_study_slot_3") + study_slot_timetable_slot_4Tostudy_slot study_slot? @relation("timetable_slot_4Tostudy_slot", fields: [slot_4], references: [id], map: "fk_timetable_study_slot_4") + study_slot_timetable_slot_5Tostudy_slot study_slot? @relation("timetable_slot_5Tostudy_slot", fields: [slot_5], references: [id], map: "fk_timetable_study_slot_5") + study_slot_timetable_slot_6Tostudy_slot study_slot? @relation("timetable_slot_6Tostudy_slot", fields: [slot_6], references: [id], map: "fk_timetable_study_slot_6") + users users @relation(fields: [teacher], references: [id], onDelete: Cascade, map: "fk_timetable_users_teacher") +} + +model users { + id Int @id(map: "pk_users") @default(autoincrement()) + login String @db.VarChar(25) + pass String? @db.VarChar(100) + is_admin Boolean? @default(false) + timetable timetable[] + user_session user_session[] +} + +model user_session { + id String @id @default(uuid()) + usersId Int + + user users @relation(fields: [usersId], references: [id]) +} diff --git a/public/uploads/.gitignore b/public/uploads/.gitignore new file mode 100644 index 0000000..4bc4044 --- /dev/null +++ b/public/uploads/.gitignore @@ -0,0 +1 @@ +*.jpg \ No newline at end of file diff --git a/public/uploads/.gitkeep b/public/uploads/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/components/Navbar.astro b/src/components/Navbar.astro new file mode 100644 index 0000000..5bb2140 --- /dev/null +++ b/src/components/Navbar.astro @@ -0,0 +1,32 @@ +--- +const items = [ + { + href: "/", + title: "Новости", + }, + { + href: "/timetable", + title: "Расписание", + }, + { + href: "/logout", + title: "Выйти", + }, +]; +--- + + diff --git a/src/components/NewsBlock.astro b/src/components/NewsBlock.astro new file mode 100644 index 0000000..6fa364d --- /dev/null +++ b/src/components/NewsBlock.astro @@ -0,0 +1,128 @@ +--- +import { getNewsAndAlerts, getSessionUser } from "../db"; + +const { news, alerts } = await getNewsAndAlerts(); + +const sessId = Astro.cookies.get("session").value!; +const user = (await getSessionUser(sessId))!; + +const formatDate = (dt: Date) => { + return ( + ("0" + dt.getDay()).substr(-2) + + "." + + ("0" + (0 + dt.getMonth())).substr(-2) + + "." + + dt.getFullYear() + + " в " + + dt.getHours() + + ":" + + ("0" + dt.getMinutes()).substr(-2) + ); +}; +--- + +
+ {user.is_admin ? ( + + Новая статья + +
+ ) : null} + { + alerts.map((article, idx) => ( +
+ +
+ )) + } + { alerts.length > 0 ?
: null } + { news.length > 0 ? + news.map((article, idx) => ( +
+ +
+ )) : +
+ +
+ } +
+{user.is_admin ? + : null} + + + \ No newline at end of file diff --git a/src/data/user.ts b/src/data/user.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/db.ts b/src/db.ts new file mode 100644 index 0000000..5887396 --- /dev/null +++ b/src/db.ts @@ -0,0 +1,146 @@ +import { PrismaClient, users } from "@prisma/client"; +import { blake3 } from "@noble/hashes/blake3"; +import { bytesToHex as toHex } from "@noble/hashes/utils"; + +const client = new PrismaClient(); + +export async function getSessionUser(sessionId: string) { + const dbSession = await client.user_session.findFirst({ + where: { + id: sessionId, + }, + }); + const user = await client.users.findFirst({ + where: { + id: dbSession!.usersId, + }, + }); + return user; +} + +export async function getUserSession(sessionId: string) { + const dbSession = await client.user_session.findFirst({ + where: { + id: sessionId, + }, + }); + return dbSession; +} + +export async function destroySession(sessionId: string) { + try { + await client.user_session.delete({ + where: { + id: sessionId, + }, + }); + } catch (e) {} +} + +export async function isThereAnyAdmins() { + const user = await client.users.findMany({ + where: { + is_admin: true, + }, + }); + return user.length > 0; +} + +export async function getUserFromAuth(login: string, password: string) { + const user = await client.users.findFirst({ + where: { + login: login, + pass: toHex(blake3(password)), + }, + }); + return user; +} + +export async function createUser(login: string, password: string) { + const anyAdmins = await isThereAnyAdmins(); + const user = await client.users.create({ + data: { + login: login, + pass: toHex(blake3(password)), + is_admin: !anyAdmins, + }, + }); + return user; +} + +export async function createSession(user: users) { + const session = await client.user_session.create({ + data: { + usersId: user!.id, + }, + }); + return session; +} + +export async function getNewsAndAlerts() { + const allNews = await client.news.findMany({ + orderBy: { + created_at: "desc", + }, + }); + const news = []; + const alerts = []; + for (let n of allNews) { + if (n.is_alert) { + alerts.push(n); + } else { + news.push(n); + } + } + return { + news, + alerts, + }; +} + +export async function deleteArticle(articleId: number) { + const result = await client.news.delete({ + where: { + id: articleId, + }, + }); + + return result !== null; +} + +export async function createArticle(title: string, data: string, isAlert: boolean) { + const article = await client.news.create({ + data: { + title: title, + message: data, + is_alert: isAlert, + }, + }); + + return article !== null; +} + +export async function updateArticle(id: number, title: string, data: string, isAlert: boolean) { + const article = await client.news.update({ + where: { + id: id, + }, + data: { + title: title, + message: data, + is_alert: isAlert, + }, + }); + + return article !== null; +} + +export async function getArticle(articleId: number) { + const article = await client.news.findFirst({ + where: { + id: articleId, + }, + }); + + return article; +} diff --git a/src/layouts/Layout.astro b/src/layouts/Layout.astro index f1a62a5..9940b46 100644 --- a/src/layouts/Layout.astro +++ b/src/layouts/Layout.astro @@ -1,6 +1,8 @@ --- +import "bootstrap/dist/css/bootstrap.css"; + export interface Props { - title: string; + title: string; } const { title } = Astro.props; @@ -8,28 +10,14 @@ const { title } = Astro.props; - - - - - - {title} - - - - + + + + + + {title} + + + + - diff --git a/src/pages/articleEditor.astro b/src/pages/articleEditor.astro new file mode 100644 index 0000000..d9d1767 --- /dev/null +++ b/src/pages/articleEditor.astro @@ -0,0 +1,199 @@ +--- +import Layout from "../layouts/Layout.astro"; +import { getSessionUser, getArticle } from "../db"; +import Navbar from "../components/Navbar.astro"; +import type { news } from "@prisma/client"; + +const sessId = Astro.cookies.get("session").value!; +const user = await getSessionUser(sessId); + +if (user === null || !user.is_admin) { + return Astro.redirect("/login"); +} + +let article: news | null = null; +const articleId = parseInt(Astro.url.searchParams.get("id") ?? ""); +if (articleId) { + article = await getArticle(articleId); +} +--- + + +
+ +
+
+ +
+ + + + +
+ { + article && article.is_alert ? ( + + ) : ( + + ) + } + +
+ +
+ +
+
+
+ + + + + + + + +
diff --git a/src/pages/articles/create.ts b/src/pages/articles/create.ts new file mode 100644 index 0000000..3d0fa95 --- /dev/null +++ b/src/pages/articles/create.ts @@ -0,0 +1,42 @@ +import type { APIContext } from "astro"; +import { createArticle, updateArticle } from "../../db"; + +export async function post({ request, url }: APIContext) { + const response: { ok: boolean; reason?: string } = { + ok: true, + }; + try { + const fd = await request.formData(); + const title = fd.get("title"); + const data = fd.get("data"); + const isAlert = fd.get("isAlert"); + const isUpdate = JSON.parse(fd.get("isUpdate")?.toString() ?? "false"); + const articleId = parseInt(fd.get("articleId")?.toString() ?? ""); + if (title === null || data === null || isAlert === null || (isUpdate && Number.isNaN(articleId))) { + throw new Error("Неправильный формат запроса"); + } + + if (isUpdate) { + const updated = await updateArticle(parseInt(articleId?.toString() ?? ""), title.toString(), data.toString(), JSON.parse(isAlert.toString())); + if (!updated) { + throw new Error("Не удалось создать"); + } + } else { + const created = await createArticle(title.toString(), data.toString(), JSON.parse(isAlert.toString())); + if (!created) { + throw new Error("Не удалось создать"); + } + } + } catch (e: any) { + response.ok = false; + if (e instanceof Error) { + response.reason = e.message; + } else { + response.reason = e.toString(); + } + } + + return { + body: JSON.stringify(response), + }; +} diff --git a/src/pages/articles/delete.ts b/src/pages/articles/delete.ts new file mode 100644 index 0000000..66a8647 --- /dev/null +++ b/src/pages/articles/delete.ts @@ -0,0 +1,30 @@ +import type { APIContext } from "astro"; +import { deleteArticle } from "../../db"; + +export async function post({ request, url }: APIContext) { + const response: { ok: boolean; reason?: string } = { + ok: true, + }; + try { + const postId = parseInt(url.searchParams.get("id") ?? ""); + if (!postId) { + throw new Error("Неправильный формат запроса"); + } + + const deleted = await deleteArticle(postId); + if (!deleted) { + throw new Error("Не удалось удалить"); + } + } catch (e: any) { + response.ok = false; + if (e instanceof Error) { + response.reason = e.message; + } else { + response.reason = e.toString(); + } + } + + return { + body: JSON.stringify(response), + }; +} diff --git a/src/pages/gate/login.ts b/src/pages/gate/login.ts new file mode 100644 index 0000000..1d624ea --- /dev/null +++ b/src/pages/gate/login.ts @@ -0,0 +1,38 @@ +import type { APIContext } from "astro"; +import { createSession, getUserFromAuth } from "../../db"; + +export async function post({ request, redirect, cookies }: APIContext) { + const response: { ok: boolean; reason?: string } = { + ok: true, + }; + + const formData = await request.formData(); + const login = formData.get("login"); + const password = formData.get("password"); + if (login === null || password === null) { + response.ok = false; + response.reason = "Не предоставлены данные для входа"; + } else { + const user = await getUserFromAuth(login!.toString(), password!.toString()); + + if (user === null) { + response.ok = false; + response.reason = "Неправильная связка логин/пароль"; + } else { + const session = await createSession(user!); + if (session !== null) { + response.ok = true; + cookies.set("session", session.id, { + path: "/", + }); + } else { + response.ok = false; + response.reason = "Не удалось создать сессию для пользователя"; + } + } + } + + return { + body: JSON.stringify(response), + }; +} diff --git a/src/pages/gate/register.ts b/src/pages/gate/register.ts new file mode 100644 index 0000000..077373a --- /dev/null +++ b/src/pages/gate/register.ts @@ -0,0 +1,29 @@ +import type { APIContext } from "astro"; +import { createUser } from "../../db"; + +export async function post({ request }: APIContext) { + const response: { ok: boolean; reason?: string } = { + ok: true, + }; + + const formData = await request.formData(); + const login = formData.get("login"); + const password = formData.get("password"); + if (login === null || password === null) { + response.ok = false; + response.reason = "Не предоставлены данные для регистрации"; + } else { + const user = await createUser(login!.toString(), password!.toString()); + + if (user === null) { + response.ok = false; + response.reason = "Невозможно зарегистрировать пользователя"; + } else { + response.ok = true; + } + } + + return { + body: JSON.stringify(response), + }; +} diff --git a/src/pages/index.astro b/src/pages/index.astro index 412cc4a..fb75604 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -1,81 +1,35 @@ --- -import Layout from '../layouts/Layout.astro'; -import Card from '../components/Card.astro'; +import Layout from "../layouts/Layout.astro"; +import type { NavbarItemType } from "astro-bootstrap"; + +import { getUserSession, getNewsAndAlerts, getSessionUser } from "../db"; +import NewsBlock from "../components/NewsBlock.astro"; +import Navbar from "../components/Navbar.astro"; + +if (Astro.cookies.has("session")) { + const sessId = Astro.cookies.get("session").value!; + const dbSess = await getUserSession(sessId); + if (dbSess === null) { + Astro.cookies.delete("session"); + return Astro.redirect("/login"); + } +} else { + return Astro.redirect("/login"); +} + +const { news, alerts } = await getNewsAndAlerts(); + +const items: NavbarItemType[] = [ + { text: "Новости", href: "/" }, + { text: "Расписание", href: "/timetable" }, +]; --- - -
-

Welcome to Astro

-

- To get started, open the directory src/pages in your project.
- Code Challenge: Tweak the "Welcome to Astro" message above. -

- -
+ +
+ + +
- + diff --git a/src/pages/login.astro b/src/pages/login.astro new file mode 100644 index 0000000..f6fafc5 --- /dev/null +++ b/src/pages/login.astro @@ -0,0 +1,48 @@ +--- +import Layout from "../layouts/Layout.astro"; + +import { getUserSession } from "../db"; + +if (Astro.cookies.has("session")) { + const sessId = Astro.cookies.get("session").value!; + const dbSess = await getUserSession(sessId); + if (dbSess !== null) { + return Astro.redirect("/"); + } + Astro.cookies.delete("session"); +} +--- + + +
+
+
+
+
+
+

Вход

+
+
+
+
+ + +
+
+ + +
+
+ +
+
+ Нет аккаунта? Зарегистрироваться +
+
+
+
+
+
+
+ + diff --git a/src/pages/logout.ts b/src/pages/logout.ts new file mode 100644 index 0000000..fc8b51f --- /dev/null +++ b/src/pages/logout.ts @@ -0,0 +1,8 @@ +import type { APIContext } from "astro"; +import { destroySession } from "../db"; + +export async function get({ redirect, cookies }: APIContext) { + const sessionId = cookies.get("session").value ?? ""; + await destroySession(sessionId); + return redirect("/"); +} diff --git a/src/pages/register.astro b/src/pages/register.astro new file mode 100644 index 0000000..76b2374 --- /dev/null +++ b/src/pages/register.astro @@ -0,0 +1,48 @@ +--- +import Layout from "../layouts/Layout.astro"; + +import { getUserSession } from "../db"; + +if (Astro.cookies.has("session")) { + const sessId = Astro.cookies.get("session").value!; + const dbSess = await getUserSession(sessId); + if (dbSess !== null) { + return Astro.redirect("/"); + } + Astro.cookies.delete("session"); +} +--- + + +
+
+
+
+
+
+

Регистрация

+
+
+
+
+ + +
+
+ + +
+
+ +
+
+ Уже есть аккаунт? Войти +
+
+
+
+
+
+
+ + diff --git a/src/pages/uploadFile.ts b/src/pages/uploadFile.ts new file mode 100644 index 0000000..511746a --- /dev/null +++ b/src/pages/uploadFile.ts @@ -0,0 +1,31 @@ +import type { APIContext } from "astro"; +import sharp from "sharp"; +import { join as joinPath } from "path"; + +function randomHash() { + return (Math.random() * 1e30).toString(16); +} + +export async function post({ request }: APIContext) { + const response: { success: number; file?: { url: string } } = { + success: 1, + }; + + try { + const fd = await request.formData(); + const imageFile = fd.get("image") as Blob; + const fName = `${randomHash()}_${imageFile.name}.jpg`; + await sharp(await imageFile.arrayBuffer()) + .jpeg({ mozjpeg: true }) + .toFile(joinPath(process.cwd(), "public", "uploads", fName)); + response.file = { + url: `/uploads/${fName}`, + }; + } catch (e) { + response.success = 0; + } + + return { + body: JSON.stringify(response), + }; +}