Compare commits
	
		
			8 Commits
		
	
	
		
			036f23b777
			...
			7aeae9020d
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 7aeae9020d | ||
| 85fa1942e9 | |||
|  | c9274a0caa | ||
| 58853f38c4 | |||
|  | 1884189fb2 | ||
| 118cadefdf | |||
| b14acab08f | |||
| 60925ee6ac | 
							
								
								
									
										223
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										223
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -11,13 +11,17 @@ | ||||
|       "dependencies": { | ||||
|         "@auth/prisma-adapter": "^2.7.2", | ||||
|         "@prisma/client": "^6.5.0", | ||||
|         "@prisma/extension-accelerate": "^2.0.0", | ||||
|         "@t3-oss/env-nextjs": "^0.12.0", | ||||
|         "@tailwindcss/typography": "^0.5.16", | ||||
|         "@tanstack/react-query": "^5.69.0", | ||||
|         "@trpc/client": "^11.0.0", | ||||
|         "@trpc/react-query": "^11.0.0", | ||||
|         "@trpc/server": "^11.0.0", | ||||
|         "cuid": "^3.0.0", | ||||
|         "dompurify": "^3.2.5", | ||||
|         "github-markdown-css": "^5.8.1", | ||||
|         "gray-matter": "^4.0.3", | ||||
|         "mermaid": "^11.6.0", | ||||
|         "minio": "^8.0.5", | ||||
|         "next": "^15.2.3", | ||||
| @ -27,7 +31,9 @@ | ||||
|         "react-hot-toast": "^2.5.2", | ||||
|         "react-markdown": "^10.1.0", | ||||
|         "rehype-raw": "^7.0.0", | ||||
|         "remark": "^15.0.1", | ||||
|         "remark-gfm": "^4.0.1", | ||||
|         "remark-html": "^16.0.1", | ||||
|         "server-only": "^0.0.1", | ||||
|         "superjson": "^2.2.1", | ||||
|         "zod": "^3.24.2" | ||||
| @ -36,6 +42,7 @@ | ||||
|         "@eslint/eslintrc": "^3.3.1", | ||||
|         "@tailwindcss/postcss": "^4.0.15", | ||||
|         "@types/busboy": "^1.5.4", | ||||
|         "@types/mime-types": "^2.1.4", | ||||
|         "@types/node": "^20.14.10", | ||||
|         "@types/react": "^19.0.0", | ||||
|         "@types/react-dom": "^19.0.0", | ||||
| @ -1541,6 +1548,17 @@ | ||||
|       "devOptional": true, | ||||
|       "license": "Apache-2.0" | ||||
|     }, | ||||
|     "node_modules/@prisma/extension-accelerate": { | ||||
|       "version": "2.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/@prisma/extension-accelerate/-/extension-accelerate-2.0.0.tgz", | ||||
|       "integrity": "sha512-8phE1FQ/sqNQM5VRnWog2jp3r+/acffIJR1D7QHPk8d/WHdKUyLhIVSKnd1Gq+5orwzzW2I0O16gvb6Uzp0PRw==", | ||||
|       "engines": { | ||||
|         "node": ">=18" | ||||
|       }, | ||||
|       "peerDependencies": { | ||||
|         "@prisma/client": ">=4.16.1" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@prisma/fetch-engine": { | ||||
|       "version": "6.6.0", | ||||
|       "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.6.0.tgz", | ||||
| @ -2301,6 +2319,13 @@ | ||||
|         "@types/unist": "*" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@types/mime-types": { | ||||
|       "version": "2.1.4", | ||||
|       "resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.4.tgz", | ||||
|       "integrity": "sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==", | ||||
|       "dev": true, | ||||
|       "license": "MIT" | ||||
|     }, | ||||
|     "node_modules/@types/ms": { | ||||
|       "version": "2.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", | ||||
| @ -3508,6 +3533,13 @@ | ||||
|       "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", | ||||
|       "license": "MIT" | ||||
|     }, | ||||
|     "node_modules/cuid": { | ||||
|       "version": "3.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/cuid/-/cuid-3.0.0.tgz", | ||||
|       "integrity": "sha512-WZYYkHdIDnaxdeP8Misq3Lah5vFjJwGuItJuV+tvMafosMzw0nF297T7mrm8IOWiPJkV6gc7sa8pzx27+w25Zg==", | ||||
|       "deprecated": "Cuid and other k-sortable and non-cryptographic ids (Ulid, ObjectId, KSUID, all UUIDs) are all insecure. Use @paralleldrive/cuid2 instead.", | ||||
|       "license": "MIT" | ||||
|     }, | ||||
|     "node_modules/cytoscape": { | ||||
|       "version": "3.32.0", | ||||
|       "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.32.0.tgz", | ||||
| @ -4883,6 +4915,19 @@ | ||||
|         "url": "https://opencollective.com/eslint" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/esprima": { | ||||
|       "version": "4.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", | ||||
|       "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", | ||||
|       "license": "BSD-2-Clause", | ||||
|       "bin": { | ||||
|         "esparse": "bin/esparse.js", | ||||
|         "esvalidate": "bin/esvalidate.js" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=4" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/esquery": { | ||||
|       "version": "1.6.0", | ||||
|       "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", | ||||
| @ -4957,6 +5002,18 @@ | ||||
|       "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", | ||||
|       "license": "MIT" | ||||
|     }, | ||||
|     "node_modules/extend-shallow": { | ||||
|       "version": "2.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", | ||||
|       "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "is-extendable": "^0.1.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=0.10.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/fast-deep-equal": { | ||||
|       "version": "3.1.3", | ||||
|       "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", | ||||
| @ -5247,6 +5304,18 @@ | ||||
|         "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/github-markdown-css": { | ||||
|       "version": "5.8.1", | ||||
|       "resolved": "https://registry.npmjs.org/github-markdown-css/-/github-markdown-css-5.8.1.tgz", | ||||
|       "integrity": "sha512-8G+PFvqigBQSWLQjyzgpa2ThD9bo7+kDsriUIidGcRhXgmcaAWUIpCZf8DavJgc+xifjbCG+GvMyWr0XMXmc7g==", | ||||
|       "license": "MIT", | ||||
|       "engines": { | ||||
|         "node": ">=10" | ||||
|       }, | ||||
|       "funding": { | ||||
|         "url": "https://github.com/sponsors/sindresorhus" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/glob-parent": { | ||||
|       "version": "6.0.2", | ||||
|       "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", | ||||
| @ -5325,6 +5394,43 @@ | ||||
|       "dev": true, | ||||
|       "license": "MIT" | ||||
|     }, | ||||
|     "node_modules/gray-matter": { | ||||
|       "version": "4.0.3", | ||||
|       "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", | ||||
|       "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "js-yaml": "^3.13.1", | ||||
|         "kind-of": "^6.0.2", | ||||
|         "section-matter": "^1.0.0", | ||||
|         "strip-bom-string": "^1.0.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=6.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/gray-matter/node_modules/argparse": { | ||||
|       "version": "1.0.10", | ||||
|       "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", | ||||
|       "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "sprintf-js": "~1.0.2" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/gray-matter/node_modules/js-yaml": { | ||||
|       "version": "3.14.1", | ||||
|       "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", | ||||
|       "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "argparse": "^1.0.7", | ||||
|         "esprima": "^4.0.0" | ||||
|       }, | ||||
|       "bin": { | ||||
|         "js-yaml": "bin/js-yaml.js" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/hachure-fill": { | ||||
|       "version": "0.5.2", | ||||
|       "resolved": "https://registry.npmjs.org/hachure-fill/-/hachure-fill-0.5.2.tgz", | ||||
| @ -5479,6 +5585,44 @@ | ||||
|         "url": "https://opencollective.com/unified" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/hast-util-sanitize": { | ||||
|       "version": "5.0.2", | ||||
|       "resolved": "https://registry.npmjs.org/hast-util-sanitize/-/hast-util-sanitize-5.0.2.tgz", | ||||
|       "integrity": "sha512-3yTWghByc50aGS7JlGhk61SPenfE/p1oaFeNwkOOyrscaOkMGrcW9+Cy/QAIOBpZxP1yqDIzFMR0+Np0i0+usg==", | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@types/hast": "^3.0.0", | ||||
|         "@ungap/structured-clone": "^1.0.0", | ||||
|         "unist-util-position": "^5.0.0" | ||||
|       }, | ||||
|       "funding": { | ||||
|         "type": "opencollective", | ||||
|         "url": "https://opencollective.com/unified" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/hast-util-to-html": { | ||||
|       "version": "9.0.5", | ||||
|       "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", | ||||
|       "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@types/hast": "^3.0.0", | ||||
|         "@types/unist": "^3.0.0", | ||||
|         "ccount": "^2.0.0", | ||||
|         "comma-separated-tokens": "^2.0.0", | ||||
|         "hast-util-whitespace": "^3.0.0", | ||||
|         "html-void-elements": "^3.0.0", | ||||
|         "mdast-util-to-hast": "^13.0.0", | ||||
|         "property-information": "^7.0.0", | ||||
|         "space-separated-tokens": "^2.0.0", | ||||
|         "stringify-entities": "^4.0.0", | ||||
|         "zwitch": "^2.0.4" | ||||
|       }, | ||||
|       "funding": { | ||||
|         "type": "opencollective", | ||||
|         "url": "https://opencollective.com/unified" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/hast-util-to-jsx-runtime": { | ||||
|       "version": "2.3.6", | ||||
|       "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", | ||||
| @ -5880,6 +6024,15 @@ | ||||
|         "url": "https://github.com/sponsors/wooorm" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/is-extendable": { | ||||
|       "version": "0.1.1", | ||||
|       "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", | ||||
|       "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", | ||||
|       "license": "MIT", | ||||
|       "engines": { | ||||
|         "node": ">=0.10.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/is-extglob": { | ||||
|       "version": "2.1.1", | ||||
|       "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", | ||||
| @ -6315,6 +6468,15 @@ | ||||
|       "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz", | ||||
|       "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==" | ||||
|     }, | ||||
|     "node_modules/kind-of": { | ||||
|       "version": "6.0.3", | ||||
|       "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", | ||||
|       "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", | ||||
|       "license": "MIT", | ||||
|       "engines": { | ||||
|         "node": ">=0.10.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/kolorist": { | ||||
|       "version": "1.8.0", | ||||
|       "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz", | ||||
| @ -8716,6 +8878,22 @@ | ||||
|         "url": "https://opencollective.com/unified" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/remark": { | ||||
|       "version": "15.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/remark/-/remark-15.0.1.tgz", | ||||
|       "integrity": "sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A==", | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@types/mdast": "^4.0.0", | ||||
|         "remark-parse": "^11.0.0", | ||||
|         "remark-stringify": "^11.0.0", | ||||
|         "unified": "^11.0.0" | ||||
|       }, | ||||
|       "funding": { | ||||
|         "type": "opencollective", | ||||
|         "url": "https://opencollective.com/unified" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/remark-gfm": { | ||||
|       "version": "4.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", | ||||
| @ -8734,6 +8912,23 @@ | ||||
|         "url": "https://opencollective.com/unified" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/remark-html": { | ||||
|       "version": "16.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/remark-html/-/remark-html-16.0.1.tgz", | ||||
|       "integrity": "sha512-B9JqA5i0qZe0Nsf49q3OXyGvyXuZFDzAP2iOFLEumymuYJITVpiH1IgsTEwTpdptDmZlMDMWeDmSawdaJIGCXQ==", | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@types/mdast": "^4.0.0", | ||||
|         "hast-util-sanitize": "^5.0.0", | ||||
|         "hast-util-to-html": "^9.0.0", | ||||
|         "mdast-util-to-hast": "^13.0.0", | ||||
|         "unified": "^11.0.0" | ||||
|       }, | ||||
|       "funding": { | ||||
|         "type": "opencollective", | ||||
|         "url": "https://opencollective.com/unified" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/remark-parse": { | ||||
|       "version": "11.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", | ||||
| @ -8974,6 +9169,19 @@ | ||||
|       "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", | ||||
|       "license": "MIT" | ||||
|     }, | ||||
|     "node_modules/section-matter": { | ||||
|       "version": "1.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", | ||||
|       "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "extend-shallow": "^2.0.1", | ||||
|         "kind-of": "^6.0.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=4" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/semver": { | ||||
|       "version": "7.7.1", | ||||
|       "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", | ||||
| @ -9219,6 +9427,12 @@ | ||||
|         "node": ">=6" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/sprintf-js": { | ||||
|       "version": "1.0.3", | ||||
|       "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", | ||||
|       "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", | ||||
|       "license": "BSD-3-Clause" | ||||
|     }, | ||||
|     "node_modules/stable-hash": { | ||||
|       "version": "0.0.5", | ||||
|       "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", | ||||
| @ -9404,6 +9618,15 @@ | ||||
|         "node": ">=4" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/strip-bom-string": { | ||||
|       "version": "1.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", | ||||
|       "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", | ||||
|       "license": "MIT", | ||||
|       "engines": { | ||||
|         "node": ">=0.10.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/strip-json-comments": { | ||||
|       "version": "3.1.1", | ||||
|       "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", | ||||
|  | ||||
| @ -23,13 +23,17 @@ | ||||
|   "dependencies": { | ||||
|     "@auth/prisma-adapter": "^2.7.2", | ||||
|     "@prisma/client": "^6.5.0", | ||||
|     "@prisma/extension-accelerate": "^2.0.0", | ||||
|     "@t3-oss/env-nextjs": "^0.12.0", | ||||
|     "@tailwindcss/typography": "^0.5.16", | ||||
|     "@tanstack/react-query": "^5.69.0", | ||||
|     "@trpc/client": "^11.0.0", | ||||
|     "@trpc/react-query": "^11.0.0", | ||||
|     "@trpc/server": "^11.0.0", | ||||
|     "cuid": "^3.0.0", | ||||
|     "dompurify": "^3.2.5", | ||||
|     "github-markdown-css": "^5.8.1", | ||||
|     "gray-matter": "^4.0.3", | ||||
|     "mermaid": "^11.6.0", | ||||
|     "minio": "^8.0.5", | ||||
|     "next": "^15.2.3", | ||||
| @ -39,7 +43,9 @@ | ||||
|     "react-hot-toast": "^2.5.2", | ||||
|     "react-markdown": "^10.1.0", | ||||
|     "rehype-raw": "^7.0.0", | ||||
|     "remark": "^15.0.1", | ||||
|     "remark-gfm": "^4.0.1", | ||||
|     "remark-html": "^16.0.1", | ||||
|     "server-only": "^0.0.1", | ||||
|     "superjson": "^2.2.1", | ||||
|     "zod": "^3.24.2" | ||||
| @ -48,6 +54,7 @@ | ||||
|     "@eslint/eslintrc": "^3.3.1", | ||||
|     "@tailwindcss/postcss": "^4.0.15", | ||||
|     "@types/busboy": "^1.5.4", | ||||
|     "@types/mime-types": "^2.1.4", | ||||
|     "@types/node": "^20.14.10", | ||||
|     "@types/react": "^19.0.0", | ||||
|     "@types/react-dom": "^19.0.0", | ||||
|  | ||||
| @ -4,10 +4,11 @@ | ||||
| generator client { | ||||
|     provider = "prisma-client-js" | ||||
|     binaryTargets = ["native", "debian-openssl-3.0.x"] | ||||
|     output   = "/app/generated/prisma-client" | ||||
| } | ||||
| 
 | ||||
| datasource db { | ||||
|     provider = "mysql" | ||||
|     provider = "postgresql" | ||||
|     // NOTE: When using mysql or sqlserver, uncomment the @db.Text annotations in model Account below | ||||
|     // Further reading: | ||||
|     // https://next-auth.js.org/adapters/prisma#create-the-prisma-schema | ||||
| @ -23,7 +24,7 @@ model Account { | ||||
|     type                     String | ||||
|     provider                 String | ||||
|     providerAccountId        String | ||||
|     refresh_token            String? @db.Text | ||||
|     refresh_token            String? //@db.Text | ||||
|     access_token             String? // @db.Text | ||||
|     expires_at               Int? | ||||
|     token_type               String? | ||||
|  | ||||
							
								
								
									
										70
									
								
								src/app/LoadingSkeleton.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								src/app/LoadingSkeleton.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,70 @@ | ||||
| import React, { Suspense } from "react"; | ||||
| import { HomeButton } from "~/app/_components/HomeButton"; // Import the client component
 | ||||
| import { Toaster } from "react-hot-toast"; | ||||
| import { | ||||
|   FileActionsContainer, | ||||
| } from "~/app/_components/ActionButtons"; // Import the client component
 | ||||
| 
 | ||||
| const LoadingSkeleton: React.FC = () => ( | ||||
|     <main className="flex min-h-screen flex-col items-center justify-center bg-gradient-to-b from-[#2e026d] to-[#15162c] text-white"> | ||||
|       <div className="absolute top-4 left-4"> | ||||
|          <HomeButton /> | ||||
|       </div> | ||||
|       <Toaster position="top-right" reverseOrder={false} /> | ||||
|       <div className="container flex flex-col items-center gap-12 px-4 py-16"> | ||||
|         <h1 className="text-5xl font-extrabold tracking-tight sm:text-[5rem]"> | ||||
|           <span className="text-[hsl(280,100%,70%)]">File</span> Details | ||||
|         </h1> | ||||
|         <div className="mt-6"> | ||||
|           <svg | ||||
|                       className="h-6 w-6 animate-spin text-white/70" | ||||
|                       xmlns="http://www.w3.org/2000/svg" | ||||
|                       fill="none" | ||||
|                       viewBox="0 0 24 24" | ||||
|                     > | ||||
|                       <circle | ||||
|                         className="opacity-25" | ||||
|                         cx="12" | ||||
|                         cy="12" | ||||
|                         r="10" | ||||
|                         stroke="currentColor" | ||||
|                         strokeWidth="4" | ||||
|                       /> | ||||
|                       <path | ||||
|                         className="opacity-75" | ||||
|                         fill="currentColor" | ||||
|                         d="M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z" | ||||
|                       /> | ||||
|                     </svg> | ||||
|         </div> | ||||
|         <div className="w-full max-w-md rounded-lg bg-white/10 p-6 text-white shadow-md"> | ||||
|           <p> | ||||
|             <strong>Name:</strong> <span className="inline-block h-6 w-24 rounded bg-white/20 animate-pulse align-middle ml-2" /> | ||||
|           </p> | ||||
|           <p> | ||||
|             <strong>Size:</strong> <span className="inline-block h-6 w-16 rounded bg-white/20 animate-pulse align-middle ml-2" /> | ||||
|           </p> | ||||
|           <p> | ||||
|             <strong>Owner:</strong> <span className="inline-block h-6 w-20 rounded bg-white/20 animate-pulse align-middle ml-2" /> | ||||
|           </p> | ||||
|           <p> | ||||
|             <strong>Upload Date:</strong> <span className="inline-block h-6 w-28 rounded bg-white/20 animate-pulse align-middle ml-2" /> | ||||
|           </p> | ||||
|           <div> | ||||
|             <strong>Description:</strong> <span className="inline-block h-6 w-40 rounded bg-white/20 animate-pulse align-middle ml-2" /> | ||||
|           </div> | ||||
|           <div className="mt-4 flex justify-center"> | ||||
|             <FileActionsContainer | ||||
|               fileId={""} | ||||
|               fileName={""} | ||||
|               fileUrl={""} | ||||
|               isOwner={false} | ||||
|               isPublic={false} | ||||
|             /> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|     </main> | ||||
| ); | ||||
| 
 | ||||
| export default LoadingSkeleton; | ||||
| @ -59,7 +59,7 @@ export function FileActionsContainer({ | ||||
|           console.error(err); | ||||
|         } | ||||
|         }} | ||||
|         className="flex items-center justify-center rounded-full bg-red-500 p-2 hover:bg-red-600" | ||||
|         className="flex items-center justify-center rounded-full bg-red-500 p-2 hover:bg-red-700" | ||||
|       > | ||||
|         <img src="/icons/delete.svg" alt="Remove" className="h-6 w-6" /> | ||||
|       </button> | ||||
|  | ||||
| @ -1,13 +1,11 @@ | ||||
| "use client"; | ||||
| 
 | ||||
| import { useEffect, useState } from "react"; | ||||
| import toast from "react-hot-toast"; | ||||
| import { useRouter } from "next/navigation"; | ||||
| import { env } from "~/env.js"; | ||||
| import { FilePreview } from "~/app/_components/FilePreview"; | ||||
| import { useFileActions } from "~/app/_components/FileActions"; | ||||
| import { FileActionsContainer } from "./ActionButtons"; | ||||
| import { checkOwner } from "~/utils/checkOwner"; // Import the client component
 | ||||
| 
 | ||||
| interface FileDetails { | ||||
|   id: string; | ||||
| @ -73,13 +71,15 @@ export default function FileGrid({ session }: FileGridProps) { | ||||
| 
 | ||||
|     const eventSource = new EventSource("/api/files/stream"); | ||||
|     eventSource.onmessage = (event) => { | ||||
|       const data: { type: string; file?: FileDetails; fileId?: string } = JSON.parse(event.data); | ||||
| 
 | ||||
|       if (data.type === "file-added" && data.file) { | ||||
|         setFiles((prevFiles) => (data.file ? [...prevFiles, data.file] : prevFiles)); | ||||
|         toast.success(`File "${data.file.name}" added!`); | ||||
|       const data: { type: string; fileId?: string } = JSON.parse(event.data); | ||||
|       console.log("SSE event:", data); | ||||
|       if (data.type === "file-added" && data.fileId) { | ||||
|         fetchFiles(); | ||||
|       } else if (data.type === "file-updated" && data.fileId) { | ||||
|         // Fetch the updated file details
 | ||||
|         fetchFiles(); | ||||
|       } else if (data.type === "file-removed" && data.fileId) { | ||||
|         setFiles((prevFiles) => prevFiles.filter((file) => file.id !== data.fileId)); | ||||
|         setFiles((prevFiles => prevFiles.filter(file => file.id !== data.fileId))); | ||||
|       } | ||||
|     }; | ||||
| 
 | ||||
| @ -105,7 +105,7 @@ export default function FileGrid({ session }: FileGridProps) { | ||||
|             key={file.id} | ||||
|             className="flex place-content-end max-w-xs flex-col gap-4 rounded-xl bg-white/10 p-4 hover:bg-white/20" | ||||
|           > | ||||
|             {<div className=" self-center max-w-50"><FilePreview fileId={file.id} fileType={file.extension} /></div>} | ||||
|             {<div className=" self-center max-w-50"><FilePreview fileId={file.id} fileType={file.extension} share={false} /></div>} | ||||
| 
 | ||||
|             <button onClick={() => router.push(pageUrl + file.url)}> | ||||
|               <h3 className="text-2xl font-bold">{file.name}</h3> | ||||
|  | ||||
| @ -1,16 +1,22 @@ | ||||
| "use client"; | ||||
| 
 | ||||
| import { useEffect, useState } from "react"; | ||||
| import { getFileType } from "~/utils/fileType"; // Adjust the import path as necessary
 | ||||
| import { remark } from 'remark'; | ||||
| import html from 'remark-html'; | ||||
| import matter from 'gray-matter'; | ||||
| import "github-markdown-css/github-markdown.css"; | ||||
| import "../styles/custom.css"; // Adjust the path as necessary
 | ||||
| import { MarkdownRenderer } from "../../components/MarkdownRenderer"; | ||||
| 
 | ||||
| interface FilePreviewProps { | ||||
|   fileId: string; | ||||
|   fileType: string; // Pass the file type as a prop
 | ||||
| } | ||||
| 
 | ||||
| export function FilePreview({ fileId, fileType }: FilePreviewProps) { | ||||
| export function FilePreview({ fileId, fileType, share }: FilePreviewProps & { share: boolean }) { | ||||
|   const [mediaSrc, setMediaSrc] = useState<string | null>(null); | ||||
|   const [error, setError] = useState<string | null>(null); | ||||
|   const [markdownContent, setMarkdownContent] = useState<string | null>(null); | ||||
| 
 | ||||
|   console.log("File Type:", fileType); | ||||
| 
 | ||||
| @ -47,20 +53,53 @@ export function FilePreview({ fileId, fileType }: FilePreviewProps) { | ||||
|     }; | ||||
|   }, [fileId]); | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     if (fileType.startsWith("markdown")) { | ||||
|       const fetchMarkdown = async () => { | ||||
|         try { | ||||
|           const result = await renderMarkdown({ id: fileId }); | ||||
|           setMarkdownContent(result.props.postData.contentHtml); | ||||
|         } catch (err) { | ||||
|           console.error("Failed to fetch markdown content:", err); | ||||
|         } | ||||
|       }; | ||||
| 
 | ||||
|       fetchMarkdown(); | ||||
|     } | ||||
|   }, [fileId, fileType]); | ||||
| 
 | ||||
| 
 | ||||
|   if (error) { | ||||
|     return <div className="text-red-500">{error}</div>; | ||||
|   } | ||||
| 
 | ||||
|   if (!mediaSrc) { | ||||
|   if (!mediaSrc && !markdownContent) { | ||||
|     return <div>Loading...</div>; | ||||
|   } | ||||
| 
 | ||||
|   if (fileType.startsWith("markdown")) { | ||||
|     if (share) { | ||||
|       return ( | ||||
|         <div className="overflow-y-auto max-h-96 rounded-lg shadow-md"> | ||||
|           {markdownContent ? ( | ||||
|             <MarkdownRenderer markdownContent={markdownContent} /> | ||||
|           ) : ( | ||||
|             <div>Loading markdown...</div> | ||||
|           )} | ||||
|         </div> | ||||
|       ); | ||||
|     } | ||||
|     return ( | ||||
|       <img src="/icons/files/code.svg" alt="Code file preview" className="max-w-full max-h-96 rounded-lg invert" /> | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   if (fileType.startsWith("video")) { | ||||
|     return ( | ||||
|       <video | ||||
|         controls | ||||
|         className="max-w-full max-h-96 rounded-lg shadow-md" | ||||
|         src={mediaSrc} | ||||
|         src={mediaSrc || ""} | ||||
|       > | ||||
|         Your browser does not support the video tag. | ||||
|       </video> | ||||
| @ -71,14 +110,14 @@ export function FilePreview({ fileId, fileType }: FilePreviewProps) { | ||||
|       <audio | ||||
|         controls | ||||
|         className="max-w-full max-h-96 rounded-lg shadow-md" | ||||
|         src={mediaSrc} | ||||
|         src={mediaSrc || ""} | ||||
|       > | ||||
|         Your browser does not support the audio tag. | ||||
|       </audio> | ||||
|     ); | ||||
|   } | ||||
|   if (fileType.startsWith("image")) { | ||||
|     return <img src={mediaSrc} alt="Media preview" className="max-w-full max-h-96 rounded-lg shadow-md" />; | ||||
|     return <img src={mediaSrc || ""} alt="Media preview" className="max-w-full max-h-96 rounded-lg shadow-md" />; | ||||
|   } | ||||
| 
 | ||||
|   if (fileType.startsWith("text")) { | ||||
| @ -91,17 +130,48 @@ export function FilePreview({ fileId, fileType }: FilePreviewProps) { | ||||
|       <img src="/icons/files/archive.svg" alt="Archive file preview" className="max-w-full max-h-96 rounded-lg invert" /> | ||||
|     ); | ||||
|   } | ||||
|   if (fileType.startsWith("code") || fileType.startsWith("markdown")) { | ||||
|   if (fileType.startsWith("code")) { | ||||
|     return ( | ||||
|       <img src="/icons/files/code.svg" alt="Code file preview" className="max-w-full max-h-96 rounded-lg invert" /> | ||||
|     ); | ||||
|   } | ||||
|   // if (fileType.startsWith("markdown")) {
 | ||||
|   //   return;      
 | ||||
|   //   }
 | ||||
| 
 | ||||
|   // log file type
 | ||||
|   console.log("Unsupported file type:", fileType); | ||||
| 
 | ||||
|   return; | ||||
|   return null; | ||||
| } | ||||
| 
 | ||||
| export async function rendererMarkdown(id: string) { | ||||
|   const fileContents = await fetch(`/api/files/serv?id=${encodeURIComponent(id)}`) | ||||
|     .then((res) => res.text()) | ||||
|     .catch((err) => { | ||||
|       console.error("Failed to fetch file contents:", err); | ||||
|       return null; | ||||
|     }); | ||||
| 
 | ||||
|   if (!fileContents) { | ||||
|     throw new Error("File contents could not be fetched."); | ||||
|   } | ||||
|   const matterResult = matter(fileContents); | ||||
| 
 | ||||
|   const processedContent = await remark() | ||||
|     .use(html) | ||||
|     .process(matterResult.content); | ||||
|   const contentHtml = processedContent.toString(); | ||||
| 
 | ||||
|   return { | ||||
|     id, | ||||
|     contentHtml, | ||||
|     ...matterResult.data, | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| export async function renderMarkdown({ id }: { id: string }) { | ||||
|   const postData = await rendererMarkdown(id); | ||||
| 
 | ||||
|   return { | ||||
|     props: { | ||||
|       postData, | ||||
|     }, | ||||
|   }; | ||||
| } | ||||
							
								
								
									
										45
									
								
								src/app/_components/GenerateMetadata.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/app/_components/GenerateMetadata.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,45 @@ | ||||
| import type { Metadata } from "next"; | ||||
| 
 | ||||
| export async function generateMetadata({ | ||||
|   searchParams, | ||||
| }: { | ||||
|   searchParams: { id?: string }; | ||||
| }): Promise<Metadata> { | ||||
|   const fileId = searchParams.id; | ||||
| 
 | ||||
|   if (!fileId) { | ||||
|     return { | ||||
|       title: "File Not Found", | ||||
|       description: "The file you are looking for does not exist.", | ||||
|     }; | ||||
|   } | ||||
| 
 | ||||
|   // Fetch file details for metadata
 | ||||
|   const response = await fetch( | ||||
|     `${process.env.NEXT_PUBLIC_PAGE_URL}/api/files/share?id=${encodeURIComponent(fileId)}`, | ||||
|     { cache: "no-store" }, | ||||
|   ); | ||||
|   if (!response.ok) { | ||||
|     return { | ||||
|       title: "File Not Found", | ||||
|       description: "The file you are looking for does not exist.", | ||||
|     }; | ||||
|   } | ||||
|   const fileDetails = await response.json(); | ||||
| 
 | ||||
|   return { | ||||
|     title: fileDetails.name, | ||||
|     description: fileDetails.description || fileDetails.name, | ||||
|     openGraph: { | ||||
|       title: fileDetails.name, | ||||
|       description: fileDetails.description || fileDetails.name, | ||||
|       url: `${process.env.NEXT_PUBLIC_PAGE_URL}/share?id=${fileDetails.id}`, | ||||
|       images: [ | ||||
|         { | ||||
|           url: `${process.env.NEXT_PUBLIC_PAGE_URL}/api/files/serv?id=${fileDetails.id}`, | ||||
|           alt: `${fileDetails.name} preview`, | ||||
|         }, | ||||
|       ], | ||||
|     }, | ||||
|   }; | ||||
| } | ||||
| @ -10,6 +10,7 @@ export default function UploadForm() { | ||||
|   const [uploadedFileUrl, setUploadedFileUrl] = useState<string | null>(null); | ||||
|   const [progress, setProgress] = useState<number>(0); // Track upload progress
 | ||||
|   const fileInputRef = useRef<HTMLInputElement | null>(null); // Ref for the file input
 | ||||
|   const [isDragActive, setIsDragActive] = useState(false); // Track drag state
 | ||||
| 
 | ||||
|   const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => { | ||||
|     if (e.target.files) { | ||||
| @ -20,6 +21,34 @@ export default function UploadForm() { | ||||
|     } | ||||
|   }; | ||||
| 
 | ||||
|   // Drag and drop handlers
 | ||||
|   const handleDragOver = (e: React.DragEvent<HTMLDivElement>) => { | ||||
|     e.preventDefault(); | ||||
|     e.stopPropagation(); | ||||
|     setIsDragActive(true); | ||||
|   }; | ||||
| 
 | ||||
|   const handleDragLeave = (e: React.DragEvent<HTMLDivElement>) => { | ||||
|     e.preventDefault(); | ||||
|     e.stopPropagation(); | ||||
|     setIsDragActive(false); | ||||
|   }; | ||||
| 
 | ||||
|   const handleDrop = (e: React.DragEvent<HTMLDivElement>) => { | ||||
|     e.preventDefault(); | ||||
|     e.stopPropagation(); | ||||
|     setIsDragActive(false); | ||||
|     if (e.dataTransfer.files && e.dataTransfer.files.length > 0) { | ||||
|       setFile(e.dataTransfer.files[0] ?? null); | ||||
|       setUploadedFileUrl(null); | ||||
|       setProgress(0); | ||||
|       setUploading(false); | ||||
|       if (fileInputRef.current) { | ||||
|         fileInputRef.current.value = ""; | ||||
|       } | ||||
|     } | ||||
|   }; | ||||
| 
 | ||||
|   const handleUpload = async () => { | ||||
|     if (!file) return toast.error("Please select a file to upload."); | ||||
|     setUploading(true); | ||||
| @ -41,15 +70,14 @@ export default function UploadForm() { | ||||
| 
 | ||||
|       xhr.onload = () => { | ||||
|         if (xhr.status === 200) { | ||||
|           const response: { url: string } = JSON.parse(xhr.responseText); // Explicitly type the response
 | ||||
|           setUploadedFileUrl(response.url); // Assume the API returns the uploaded file URL
 | ||||
|           notifyClients({type: "file-uploaded", fileUrl: response.url}); // Notify other clients about the new file
 | ||||
|           const response = JSON.parse(xhr.responseText); | ||||
|           setUploadedFileUrl(response.file?.url || null); // Use the new response structure
 | ||||
|           toast.success("File uploaded successfully!"); | ||||
| 
 | ||||
|           // Clear the file input and reset state
 | ||||
|           setFile(null); | ||||
|           if (fileInputRef.current) { | ||||
|             fileInputRef.current.value = ""; // Clear the file input
 | ||||
|             fileInputRef.current.value = ""; | ||||
|           } | ||||
|         } else { | ||||
|           console.error("Upload failed:", xhr.responseText); | ||||
| @ -86,42 +114,48 @@ export default function UploadForm() { | ||||
|       {/* Toast container */} | ||||
|       <Toaster position="top-right" reverseOrder={false} /> | ||||
| 
 | ||||
|       <div className="flex flex-row items-center gap-4"> | ||||
|         {/* Custom file input */} | ||||
|         <label | ||||
|           htmlFor="file-upload" | ||||
|           className="cursor-pointer flex items-center gap-2 rounded-full bg-white/10 px-10 py-3 font-semibold no-underline transition hover:bg-white/20" | ||||
|         > | ||||
|           {file ? ( | ||||
|             <> | ||||
|               File Selected | ||||
|               {/* SVG Icon */} | ||||
|               <svg | ||||
|                 xmlns="http://www.w3.org/2000/svg" | ||||
|                 fill="none" | ||||
|                 viewBox="0 0 24 24" | ||||
|                 strokeWidth={2} | ||||
|                 stroke="currentColor" | ||||
|                 className="h-5 w-5 text-green-500" | ||||
|               > | ||||
|                 <path | ||||
|                   strokeLinecap="round" | ||||
|                   strokeLinejoin="round" | ||||
|                   d="M5 13l4 4L19 7" | ||||
|                 /> | ||||
|               </svg> | ||||
|             </> | ||||
|           ) : ( | ||||
|             "Select File" | ||||
|           )} | ||||
|         </label> | ||||
|       {/* Drag and Drop Area */} | ||||
|       <div | ||||
|         className={`w-full max-w-md flex flex-col items-center justify-center border-2 border-dashed rounded-lg p-6 mb-2 transition-colors duration-200 ${isDragActive ? "border-blue-500 bg-blue-100/30" : "border-gray-400 bg-transparent hover:bg-gray-50/10"}`} | ||||
|         onDragOver={handleDragOver} | ||||
|         onDragLeave={handleDragLeave} | ||||
|         onDrop={handleDrop} | ||||
|         onClick={() => fileInputRef.current?.click()} | ||||
|         style={{ cursor: "pointer" }} | ||||
|       > | ||||
|         {/* Hidden file input for click-to-select */} | ||||
|         <input | ||||
|           id="file-upload" | ||||
|           ref={fileInputRef} // Attach the ref to the file input
 | ||||
|           type="file" | ||||
|           ref={fileInputRef} | ||||
|           style={{ display: "none" }} | ||||
|           onChange={handleFileChange} | ||||
|           className="hidden" // Hide the default file input
 | ||||
|         /> | ||||
|         <span className="text-gray-300"> | ||||
|           {isDragActive ? "Drop your file here" : "Drag & drop a file here, or click to select"} | ||||
|         </span> | ||||
|         {file && ( | ||||
|           <div className="mt-2 flex items-center gap-2"> | ||||
|         <span className="text-green-500 font-semibold">{file.name}</span> | ||||
|         {/* Add button to remove file */} | ||||
|         <button | ||||
|           onClick={e => { | ||||
|             e.stopPropagation(); | ||||
|             setFile(null); | ||||
|             if (fileInputRef.current) { | ||||
|           fileInputRef.current.value = ""; | ||||
|             } | ||||
|           }} | ||||
|           className="flex items-center justify-center rounded-full bg-red-500 p-2 hover:bg-red-700" | ||||
|           style={{ cursor: "pointer" }} | ||||
|         > | ||||
|           <img src="/icons/delete.svg" alt="Remove" className="h-6 w-6" /> | ||||
|         </button> | ||||
|           </div> | ||||
|         )} | ||||
|       </div> | ||||
|       {/* Show upload button only when file is selected */} | ||||
|       {file && ( | ||||
|       <div className="flex flex-row items-center gap-4"> | ||||
|         <button | ||||
|           onClick={handleUpload} | ||||
|           disabled={uploading || !file} | ||||
| @ -129,7 +163,7 @@ export default function UploadForm() { | ||||
|         > | ||||
|           {uploading ? "Uploading..." : "Upload"} | ||||
|         </button> | ||||
|       </div> | ||||
|       </div>)} | ||||
| 
 | ||||
|       {file && uploading && ( | ||||
|         <div className="w-full max-w-md flex items-center gap-2"> | ||||
| @ -142,31 +176,17 @@ export default function UploadForm() { | ||||
|         </div> | ||||
|       )} | ||||
| 
 | ||||
|       {uploadedFileUrl && ( | ||||
|       {/* {uploadedFileUrl && file && ( | ||||
|         <div className="flex flex-row items-center gap-4"> | ||||
|           <p className="text-white">{uploadedFileUrl}</p> | ||||
|           <p className="text-white">{file.name}</p> | ||||
|           <button | ||||
|             onClick={handleCopyUrl} | ||||
|             className="flex items-center justify-center rounded-full bg-blue-500 p-2 hover:bg-blue-600" | ||||
|           > | ||||
|             {/* Copy Icon */} | ||||
|             <svg | ||||
|               xmlns="http://www.w3.org/2000/svg" | ||||
|               fill="none" | ||||
|               viewBox="0 0 24 24" | ||||
|               strokeWidth={2} | ||||
|               stroke="currentColor" | ||||
|               className="h-5 w-5 text-white" | ||||
|             > | ||||
|               <path | ||||
|                 strokeLinecap="round" | ||||
|                 strokeLinejoin="round" | ||||
|                 d="M8 16h8M8 12h8m-7 8h6a2 2 0 002-2V6a2 2 0 00-2-2H9a2 2 0 00-2 2v12a2 2 0 002 2z" | ||||
|               /> | ||||
|             </svg> | ||||
|             <img src="/icons/copy.svg" alt="Copy URL" className="h-6 w-6" /> | ||||
|           </button> | ||||
|         </div> | ||||
|       )} | ||||
|       )} */} | ||||
|     </div> | ||||
|   ); | ||||
| } | ||||
| @ -1,10 +1,12 @@ | ||||
| import { NextResponse } from "next/server"; | ||||
| import Busboy from "busboy"; | ||||
| import { Readable } from "stream"; | ||||
| import crypto from "crypto"; | ||||
| import { db } from "~/server/db"; | ||||
| import { auth } from "~/server/auth"; | ||||
| import { minioClient, ensureBucketExists } from "~/utils/minioClient"; | ||||
| import { getFileType } from "~/utils/fileType"; | ||||
| import cuid from 'cuid'; | ||||
| import { notifyClients } from "~/utils/notifyClients"; | ||||
| 
 | ||||
| export const config = { | ||||
|   api: { | ||||
| @ -22,7 +24,9 @@ export async function POST(req: Request) { | ||||
|   await ensureBucketExists(bucketName); | ||||
| 
 | ||||
|   return new Promise<Response>((resolve, reject) => { | ||||
|     const busboy = Busboy({ headers: { "content-type": req.headers.get("content-type") ?? "" } }); | ||||
|     const busboy = Busboy({ | ||||
|       headers: { "content-type": req.headers.get("content-type") ?? "" }, | ||||
|     }); | ||||
|     let fileName = ""; | ||||
|     let fileBuffer = Buffer.alloc(0); | ||||
| 
 | ||||
| @ -38,8 +42,11 @@ export async function POST(req: Request) { | ||||
|         fileBuffer = Buffer.concat(chunks); | ||||
| 
 | ||||
|         // Generate a unique ID for the file
 | ||||
|         const fileId = crypto.randomUUID(); | ||||
|         const fileId = session.user.id + "-" + cuid() | ||||
|         const objectName = `${fileId}-${fileName}`; | ||||
|         // Change UUID to CUID
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|         try { | ||||
|           // Upload the file to MinIO
 | ||||
| @ -52,12 +59,19 @@ export async function POST(req: Request) { | ||||
|               url: `/share?id=${fileId}`, | ||||
|               name: fileName, | ||||
|               size: fileBuffer.length, | ||||
|               extension: info.mimeType, | ||||
|               extension: getFileType(fileName), | ||||
|               uploadedById: session.user.id, | ||||
|             }, | ||||
|           }); | ||||
|           notifyClients({ type: "file-added", fileId: fileId }); | ||||
| 
 | ||||
|           resolve(NextResponse.json({ message: "File uploaded successfully", file: newFile })); | ||||
|           resolve( | ||||
|             NextResponse.json({ | ||||
|               message: "File uploaded successfully", | ||||
|               file: newFile, | ||||
|               fileId: fileId, | ||||
|             }), | ||||
|           ); | ||||
|         } catch (error) { | ||||
|           console.error("Error uploading file to MinIO:", error); | ||||
|           reject(new Error("Failed to upload file")); | ||||
| @ -86,4 +100,4 @@ export async function POST(req: Request) { | ||||
|       nodeStream.pipe(busboy); | ||||
|     } | ||||
|   }); | ||||
| } | ||||
| } | ||||
|  | ||||
							
								
								
									
										1
									
								
								src/app/generated/prisma-client/client.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/app/generated/prisma-client/client.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| export * from "./index" | ||||
							
								
								
									
										4
									
								
								src/app/generated/prisma-client/client.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								src/app/generated/prisma-client/client.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | ||||
| 
 | ||||
| /* !!! This is code generated by Prisma. Do not edit directly. !!! | ||||
| /* eslint-disable */ | ||||
| module.exports = { ...require('.') } | ||||
							
								
								
									
										1
									
								
								src/app/generated/prisma-client/default.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/app/generated/prisma-client/default.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| export * from "./index" | ||||
							
								
								
									
										4
									
								
								src/app/generated/prisma-client/default.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								src/app/generated/prisma-client/default.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | ||||
| 
 | ||||
| /* !!! This is code generated by Prisma. Do not edit directly. !!! | ||||
| /* eslint-disable */ | ||||
| module.exports = { ...require('.') } | ||||
							
								
								
									
										1
									
								
								src/app/generated/prisma-client/edge.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/app/generated/prisma-client/edge.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| export * from "./default" | ||||
							
								
								
									
										243
									
								
								src/app/generated/prisma-client/edge.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										243
									
								
								src/app/generated/prisma-client/edge.js
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										225
									
								
								src/app/generated/prisma-client/index-browser.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										225
									
								
								src/app/generated/prisma-client/index-browser.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,225 @@ | ||||
| 
 | ||||
| /* !!! This is code generated by Prisma. Do not edit directly. !!! | ||||
| /* eslint-disable */ | ||||
| 
 | ||||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||||
| 
 | ||||
| const { | ||||
|   Decimal, | ||||
|   objectEnumValues, | ||||
|   makeStrictEnum, | ||||
|   Public, | ||||
|   getRuntime, | ||||
|   skip | ||||
| } = require('./runtime/index-browser.js') | ||||
| 
 | ||||
| 
 | ||||
| const Prisma = {} | ||||
| 
 | ||||
| exports.Prisma = Prisma | ||||
| exports.$Enums = {} | ||||
| 
 | ||||
| /** | ||||
|  * Prisma Client JS version: 6.8.2 | ||||
|  * Query Engine version: 2060c79ba17c6bb9f5823312b6f6b7f4a845738e | ||||
|  */ | ||||
| Prisma.prismaVersion = { | ||||
|   client: "6.8.2", | ||||
|   engine: "2060c79ba17c6bb9f5823312b6f6b7f4a845738e" | ||||
| } | ||||
| 
 | ||||
| Prisma.PrismaClientKnownRequestError = () => { | ||||
|   const runtimeName = getRuntime().prettyName; | ||||
|   throw new Error(`PrismaClientKnownRequestError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
 | ||||
| In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
 | ||||
| )}; | ||||
| Prisma.PrismaClientUnknownRequestError = () => { | ||||
|   const runtimeName = getRuntime().prettyName; | ||||
|   throw new Error(`PrismaClientUnknownRequestError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
 | ||||
| In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
 | ||||
| )} | ||||
| Prisma.PrismaClientRustPanicError = () => { | ||||
|   const runtimeName = getRuntime().prettyName; | ||||
|   throw new Error(`PrismaClientRustPanicError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
 | ||||
| In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
 | ||||
| )} | ||||
| Prisma.PrismaClientInitializationError = () => { | ||||
|   const runtimeName = getRuntime().prettyName; | ||||
|   throw new Error(`PrismaClientInitializationError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
 | ||||
| In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
 | ||||
| )} | ||||
| Prisma.PrismaClientValidationError = () => { | ||||
|   const runtimeName = getRuntime().prettyName; | ||||
|   throw new Error(`PrismaClientValidationError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
 | ||||
| In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
 | ||||
| )} | ||||
| Prisma.Decimal = Decimal | ||||
| 
 | ||||
| /** | ||||
|  * Re-export of sql-template-tag | ||||
|  */ | ||||
| Prisma.sql = () => { | ||||
|   const runtimeName = getRuntime().prettyName; | ||||
|   throw new Error(`sqltag is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
 | ||||
| In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
 | ||||
| )} | ||||
| Prisma.empty = () => { | ||||
|   const runtimeName = getRuntime().prettyName; | ||||
|   throw new Error(`empty is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
 | ||||
| In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
 | ||||
| )} | ||||
| Prisma.join = () => { | ||||
|   const runtimeName = getRuntime().prettyName; | ||||
|   throw new Error(`join is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
 | ||||
| In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
 | ||||
| )} | ||||
| Prisma.raw = () => { | ||||
|   const runtimeName = getRuntime().prettyName; | ||||
|   throw new Error(`raw is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
 | ||||
| In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
 | ||||
| )} | ||||
| Prisma.validator = Public.validator | ||||
| 
 | ||||
| /** | ||||
| * Extensions | ||||
| */ | ||||
| Prisma.getExtensionContext = () => { | ||||
|   const runtimeName = getRuntime().prettyName; | ||||
|   throw new Error(`Extensions.getExtensionContext is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
 | ||||
| In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
 | ||||
| )} | ||||
| Prisma.defineExtension = () => { | ||||
|   const runtimeName = getRuntime().prettyName; | ||||
|   throw new Error(`Extensions.defineExtension is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
 | ||||
| In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
 | ||||
| )} | ||||
| 
 | ||||
| /** | ||||
|  * Shorthand utilities for JSON filtering | ||||
|  */ | ||||
| Prisma.DbNull = objectEnumValues.instances.DbNull | ||||
| Prisma.JsonNull = objectEnumValues.instances.JsonNull | ||||
| Prisma.AnyNull = objectEnumValues.instances.AnyNull | ||||
| 
 | ||||
| Prisma.NullTypes = { | ||||
|   DbNull: objectEnumValues.classes.DbNull, | ||||
|   JsonNull: objectEnumValues.classes.JsonNull, | ||||
|   AnyNull: objectEnumValues.classes.AnyNull | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * Enums | ||||
|  */ | ||||
| 
 | ||||
| exports.Prisma.TransactionIsolationLevel = makeStrictEnum({ | ||||
|   ReadUncommitted: 'ReadUncommitted', | ||||
|   ReadCommitted: 'ReadCommitted', | ||||
|   RepeatableRead: 'RepeatableRead', | ||||
|   Serializable: 'Serializable' | ||||
| }); | ||||
| 
 | ||||
| exports.Prisma.AccountScalarFieldEnum = { | ||||
|   id: 'id', | ||||
|   userId: 'userId', | ||||
|   type: 'type', | ||||
|   provider: 'provider', | ||||
|   providerAccountId: 'providerAccountId', | ||||
|   refresh_token: 'refresh_token', | ||||
|   access_token: 'access_token', | ||||
|   expires_at: 'expires_at', | ||||
|   token_type: 'token_type', | ||||
|   scope: 'scope', | ||||
|   id_token: 'id_token', | ||||
|   session_state: 'session_state', | ||||
|   refresh_token_expires_in: 'refresh_token_expires_in' | ||||
| }; | ||||
| 
 | ||||
| exports.Prisma.SessionScalarFieldEnum = { | ||||
|   id: 'id', | ||||
|   sessionToken: 'sessionToken', | ||||
|   userId: 'userId', | ||||
|   expires: 'expires' | ||||
| }; | ||||
| 
 | ||||
| exports.Prisma.UserScalarFieldEnum = { | ||||
|   id: 'id', | ||||
|   name: 'name', | ||||
|   email: 'email', | ||||
|   emailVerified: 'emailVerified', | ||||
|   image: 'image' | ||||
| }; | ||||
| 
 | ||||
| exports.Prisma.VerificationTokenScalarFieldEnum = { | ||||
|   identifier: 'identifier', | ||||
|   token: 'token', | ||||
|   expires: 'expires' | ||||
| }; | ||||
| 
 | ||||
| exports.Prisma.FileScalarFieldEnum = { | ||||
|   id: 'id', | ||||
|   url: 'url', | ||||
|   name: 'name', | ||||
|   size: 'size', | ||||
|   extension: 'extension', | ||||
|   uploadDate: 'uploadDate', | ||||
|   description: 'description', | ||||
|   uploadedById: 'uploadedById', | ||||
|   public: 'public' | ||||
| }; | ||||
| 
 | ||||
| exports.Prisma.SortOrder = { | ||||
|   asc: 'asc', | ||||
|   desc: 'desc' | ||||
| }; | ||||
| 
 | ||||
| exports.Prisma.QueryMode = { | ||||
|   default: 'default', | ||||
|   insensitive: 'insensitive' | ||||
| }; | ||||
| 
 | ||||
| exports.Prisma.NullsOrder = { | ||||
|   first: 'first', | ||||
|   last: 'last' | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| exports.Prisma.ModelName = { | ||||
|   Account: 'Account', | ||||
|   Session: 'Session', | ||||
|   User: 'User', | ||||
|   VerificationToken: 'VerificationToken', | ||||
|   File: 'File' | ||||
| }; | ||||
| 
 | ||||
| /** | ||||
|  * This is a stub Prisma Client that will error at runtime if called. | ||||
|  */ | ||||
| class PrismaClient { | ||||
|   constructor() { | ||||
|     return new Proxy(this, { | ||||
|       get(target, prop) { | ||||
|         let message | ||||
|         const runtime = getRuntime() | ||||
|         if (runtime.isEdge) { | ||||
|           message = `PrismaClient is not configured to run in ${runtime.prettyName}. In order to run Prisma Client on edge runtime, either:
 | ||||
| - Use Prisma Accelerate: https://pris.ly/d/accelerate
 | ||||
| - Use Driver Adapters: https://pris.ly/d/driver-adapters
 | ||||
| `;
 | ||||
|         } else { | ||||
|           message = 'PrismaClient is unable to run in this browser environment, or has been bundled for the browser (running in `' + runtime.prettyName + '`).' | ||||
|         } | ||||
| 
 | ||||
|         message += ` | ||||
| If this is unexpected, please open an issue: https://pris.ly/prisma-prisma-bug-report`
 | ||||
| 
 | ||||
|         throw new Error(message) | ||||
|       } | ||||
|     }) | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| exports.PrismaClient = PrismaClient | ||||
| 
 | ||||
| Object.assign(exports, Prisma) | ||||
							
								
								
									
										8947
									
								
								src/app/generated/prisma-client/index.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										8947
									
								
								src/app/generated/prisma-client/index.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										258
									
								
								src/app/generated/prisma-client/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										258
									
								
								src/app/generated/prisma-client/index.js
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										140
									
								
								src/app/generated/prisma-client/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								src/app/generated/prisma-client/package.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,140 @@ | ||||
| { | ||||
|   "name": "prisma-client-08d22f6c857ed95dda3e967008d87d351c30e87c75486e96dad4c24294f1763e", | ||||
|   "main": "index.js", | ||||
|   "types": "index.d.ts", | ||||
|   "browser": "index-browser.js", | ||||
|   "exports": { | ||||
|     "./client": { | ||||
|       "require": { | ||||
|         "node": "./index.js", | ||||
|         "edge-light": "./wasm.js", | ||||
|         "workerd": "./wasm.js", | ||||
|         "worker": "./wasm.js", | ||||
|         "browser": "./index-browser.js", | ||||
|         "default": "./index.js" | ||||
|       }, | ||||
|       "import": { | ||||
|         "node": "./index.js", | ||||
|         "edge-light": "./wasm.js", | ||||
|         "workerd": "./wasm.js", | ||||
|         "worker": "./wasm.js", | ||||
|         "browser": "./index-browser.js", | ||||
|         "default": "./index.js" | ||||
|       }, | ||||
|       "default": "./index.js" | ||||
|     }, | ||||
|     "./package.json": "./package.json", | ||||
|     ".": { | ||||
|       "require": { | ||||
|         "node": "./index.js", | ||||
|         "edge-light": "./wasm.js", | ||||
|         "workerd": "./wasm.js", | ||||
|         "worker": "./wasm.js", | ||||
|         "browser": "./index-browser.js", | ||||
|         "default": "./index.js" | ||||
|       }, | ||||
|       "import": { | ||||
|         "node": "./index.js", | ||||
|         "edge-light": "./wasm.js", | ||||
|         "workerd": "./wasm.js", | ||||
|         "worker": "./wasm.js", | ||||
|         "browser": "./index-browser.js", | ||||
|         "default": "./index.js" | ||||
|       }, | ||||
|       "default": "./index.js" | ||||
|     }, | ||||
|     "./edge": { | ||||
|       "types": "./edge.d.ts", | ||||
|       "require": "./edge.js", | ||||
|       "import": "./edge.js", | ||||
|       "default": "./edge.js" | ||||
|     }, | ||||
|     "./react-native": { | ||||
|       "types": "./react-native.d.ts", | ||||
|       "require": "./react-native.js", | ||||
|       "import": "./react-native.js", | ||||
|       "default": "./react-native.js" | ||||
|     }, | ||||
|     "./extension": { | ||||
|       "types": "./extension.d.ts", | ||||
|       "require": "./extension.js", | ||||
|       "import": "./extension.js", | ||||
|       "default": "./extension.js" | ||||
|     }, | ||||
|     "./index-browser": { | ||||
|       "types": "./index.d.ts", | ||||
|       "require": "./index-browser.js", | ||||
|       "import": "./index-browser.js", | ||||
|       "default": "./index-browser.js" | ||||
|     }, | ||||
|     "./index": { | ||||
|       "types": "./index.d.ts", | ||||
|       "require": "./index.js", | ||||
|       "import": "./index.js", | ||||
|       "default": "./index.js" | ||||
|     }, | ||||
|     "./wasm": { | ||||
|       "types": "./wasm.d.ts", | ||||
|       "require": "./wasm.js", | ||||
|       "import": "./wasm.mjs", | ||||
|       "default": "./wasm.mjs" | ||||
|     }, | ||||
|     "./runtime/client": { | ||||
|       "types": "./runtime/client.d.ts", | ||||
|       "require": "./runtime/client.js", | ||||
|       "import": "./runtime/client.mjs", | ||||
|       "default": "./runtime/client.mjs" | ||||
|     }, | ||||
|     "./runtime/library": { | ||||
|       "types": "./runtime/library.d.ts", | ||||
|       "require": "./runtime/library.js", | ||||
|       "import": "./runtime/library.mjs", | ||||
|       "default": "./runtime/library.mjs" | ||||
|     }, | ||||
|     "./runtime/binary": { | ||||
|       "types": "./runtime/binary.d.ts", | ||||
|       "require": "./runtime/binary.js", | ||||
|       "import": "./runtime/binary.mjs", | ||||
|       "default": "./runtime/binary.mjs" | ||||
|     }, | ||||
|     "./runtime/wasm": { | ||||
|       "types": "./runtime/wasm.d.ts", | ||||
|       "require": "./runtime/wasm.js", | ||||
|       "import": "./runtime/wasm.mjs", | ||||
|       "default": "./runtime/wasm.mjs" | ||||
|     }, | ||||
|     "./runtime/edge": { | ||||
|       "types": "./runtime/edge.d.ts", | ||||
|       "require": "./runtime/edge.js", | ||||
|       "import": "./runtime/edge-esm.js", | ||||
|       "default": "./runtime/edge-esm.js" | ||||
|     }, | ||||
|     "./runtime/react-native": { | ||||
|       "types": "./runtime/react-native.d.ts", | ||||
|       "require": "./runtime/react-native.js", | ||||
|       "import": "./runtime/react-native.js", | ||||
|       "default": "./runtime/react-native.js" | ||||
|     }, | ||||
|     "./generator-build": { | ||||
|       "require": "./generator-build/index.js", | ||||
|       "import": "./generator-build/index.js", | ||||
|       "default": "./generator-build/index.js" | ||||
|     }, | ||||
|     "./sql": { | ||||
|       "require": { | ||||
|         "types": "./sql.d.ts", | ||||
|         "node": "./sql.js", | ||||
|         "default": "./sql.js" | ||||
|       }, | ||||
|       "import": { | ||||
|         "types": "./sql.d.ts", | ||||
|         "node": "./sql.mjs", | ||||
|         "default": "./sql.mjs" | ||||
|       }, | ||||
|       "default": "./sql.js" | ||||
|     }, | ||||
|     "./*": "./*" | ||||
|   }, | ||||
|   "version": "6.8.2", | ||||
|   "sideEffects": false | ||||
| } | ||||
							
								
								
									
										34
									
								
								src/app/generated/prisma-client/runtime/edge-esm.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/app/generated/prisma-client/runtime/edge-esm.js
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										34
									
								
								src/app/generated/prisma-client/runtime/edge.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/app/generated/prisma-client/runtime/edge.js
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										370
									
								
								src/app/generated/prisma-client/runtime/index-browser.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										370
									
								
								src/app/generated/prisma-client/runtime/index-browser.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,370 @@ | ||||
| declare class AnyNull extends NullTypesEnumValue { | ||||
|     #private; | ||||
| } | ||||
| 
 | ||||
| declare type Args<T, F extends Operation> = T extends { | ||||
|     [K: symbol]: { | ||||
|         types: { | ||||
|             operations: { | ||||
|                 [K in F]: { | ||||
|                     args: any; | ||||
|                 }; | ||||
|             }; | ||||
|         }; | ||||
|     }; | ||||
| } ? T[symbol]['types']['operations'][F]['args'] : any; | ||||
| 
 | ||||
| declare class DbNull extends NullTypesEnumValue { | ||||
|     #private; | ||||
| } | ||||
| 
 | ||||
| export declare function Decimal(n: Decimal.Value): Decimal; | ||||
| 
 | ||||
| export declare namespace Decimal { | ||||
|     export type Constructor = typeof Decimal; | ||||
|     export type Instance = Decimal; | ||||
|     export type Rounding = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8; | ||||
|     export type Modulo = Rounding | 9; | ||||
|     export type Value = string | number | Decimal; | ||||
| 
 | ||||
|     // http://mikemcl.github.io/decimal.js/#constructor-properties
 | ||||
|     export interface Config { | ||||
|         precision?: number; | ||||
|         rounding?: Rounding; | ||||
|         toExpNeg?: number; | ||||
|         toExpPos?: number; | ||||
|         minE?: number; | ||||
|         maxE?: number; | ||||
|         crypto?: boolean; | ||||
|         modulo?: Modulo; | ||||
|         defaults?: boolean; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export declare class Decimal { | ||||
|     readonly d: number[]; | ||||
|     readonly e: number; | ||||
|     readonly s: number; | ||||
| 
 | ||||
|     constructor(n: Decimal.Value); | ||||
| 
 | ||||
|     absoluteValue(): Decimal; | ||||
|     abs(): Decimal; | ||||
| 
 | ||||
|     ceil(): Decimal; | ||||
| 
 | ||||
|     clampedTo(min: Decimal.Value, max: Decimal.Value): Decimal; | ||||
|     clamp(min: Decimal.Value, max: Decimal.Value): Decimal; | ||||
| 
 | ||||
|     comparedTo(n: Decimal.Value): number; | ||||
|     cmp(n: Decimal.Value): number; | ||||
| 
 | ||||
|     cosine(): Decimal; | ||||
|     cos(): Decimal; | ||||
| 
 | ||||
|     cubeRoot(): Decimal; | ||||
|     cbrt(): Decimal; | ||||
| 
 | ||||
|     decimalPlaces(): number; | ||||
|     dp(): number; | ||||
| 
 | ||||
|     dividedBy(n: Decimal.Value): Decimal; | ||||
|     div(n: Decimal.Value): Decimal; | ||||
| 
 | ||||
|     dividedToIntegerBy(n: Decimal.Value): Decimal; | ||||
|     divToInt(n: Decimal.Value): Decimal; | ||||
| 
 | ||||
|     equals(n: Decimal.Value): boolean; | ||||
|     eq(n: Decimal.Value): boolean; | ||||
| 
 | ||||
|     floor(): Decimal; | ||||
| 
 | ||||
|     greaterThan(n: Decimal.Value): boolean; | ||||
|     gt(n: Decimal.Value): boolean; | ||||
| 
 | ||||
|     greaterThanOrEqualTo(n: Decimal.Value): boolean; | ||||
|     gte(n: Decimal.Value): boolean; | ||||
| 
 | ||||
|     hyperbolicCosine(): Decimal; | ||||
|     cosh(): Decimal; | ||||
| 
 | ||||
|     hyperbolicSine(): Decimal; | ||||
|     sinh(): Decimal; | ||||
| 
 | ||||
|     hyperbolicTangent(): Decimal; | ||||
|     tanh(): Decimal; | ||||
| 
 | ||||
|     inverseCosine(): Decimal; | ||||
|     acos(): Decimal; | ||||
| 
 | ||||
|     inverseHyperbolicCosine(): Decimal; | ||||
|     acosh(): Decimal; | ||||
| 
 | ||||
|     inverseHyperbolicSine(): Decimal; | ||||
|     asinh(): Decimal; | ||||
| 
 | ||||
|     inverseHyperbolicTangent(): Decimal; | ||||
|     atanh(): Decimal; | ||||
| 
 | ||||
|     inverseSine(): Decimal; | ||||
|     asin(): Decimal; | ||||
| 
 | ||||
|     inverseTangent(): Decimal; | ||||
|     atan(): Decimal; | ||||
| 
 | ||||
|     isFinite(): boolean; | ||||
| 
 | ||||
|     isInteger(): boolean; | ||||
|     isInt(): boolean; | ||||
| 
 | ||||
|     isNaN(): boolean; | ||||
| 
 | ||||
|     isNegative(): boolean; | ||||
|     isNeg(): boolean; | ||||
| 
 | ||||
|     isPositive(): boolean; | ||||
|     isPos(): boolean; | ||||
| 
 | ||||
|     isZero(): boolean; | ||||
| 
 | ||||
|     lessThan(n: Decimal.Value): boolean; | ||||
|     lt(n: Decimal.Value): boolean; | ||||
| 
 | ||||
|     lessThanOrEqualTo(n: Decimal.Value): boolean; | ||||
|     lte(n: Decimal.Value): boolean; | ||||
| 
 | ||||
|     logarithm(n?: Decimal.Value): Decimal; | ||||
|     log(n?: Decimal.Value): Decimal; | ||||
| 
 | ||||
|     minus(n: Decimal.Value): Decimal; | ||||
|     sub(n: Decimal.Value): Decimal; | ||||
| 
 | ||||
|     modulo(n: Decimal.Value): Decimal; | ||||
|     mod(n: Decimal.Value): Decimal; | ||||
| 
 | ||||
|     naturalExponential(): Decimal; | ||||
|     exp(): Decimal; | ||||
| 
 | ||||
|     naturalLogarithm(): Decimal; | ||||
|     ln(): Decimal; | ||||
| 
 | ||||
|     negated(): Decimal; | ||||
|     neg(): Decimal; | ||||
| 
 | ||||
|     plus(n: Decimal.Value): Decimal; | ||||
|     add(n: Decimal.Value): Decimal; | ||||
| 
 | ||||
|     precision(includeZeros?: boolean): number; | ||||
|     sd(includeZeros?: boolean): number; | ||||
| 
 | ||||
|     round(): Decimal; | ||||
| 
 | ||||
|     sine() : Decimal; | ||||
|     sin() : Decimal; | ||||
| 
 | ||||
|     squareRoot(): Decimal; | ||||
|     sqrt(): Decimal; | ||||
| 
 | ||||
|     tangent() : Decimal; | ||||
|     tan() : Decimal; | ||||
| 
 | ||||
|     times(n: Decimal.Value): Decimal; | ||||
|     mul(n: Decimal.Value) : Decimal; | ||||
| 
 | ||||
|     toBinary(significantDigits?: number): string; | ||||
|     toBinary(significantDigits: number, rounding: Decimal.Rounding): string; | ||||
| 
 | ||||
|     toDecimalPlaces(decimalPlaces?: number): Decimal; | ||||
|     toDecimalPlaces(decimalPlaces: number, rounding: Decimal.Rounding): Decimal; | ||||
|     toDP(decimalPlaces?: number): Decimal; | ||||
|     toDP(decimalPlaces: number, rounding: Decimal.Rounding): Decimal; | ||||
| 
 | ||||
|     toExponential(decimalPlaces?: number): string; | ||||
|     toExponential(decimalPlaces: number, rounding: Decimal.Rounding): string; | ||||
| 
 | ||||
|     toFixed(decimalPlaces?: number): string; | ||||
|     toFixed(decimalPlaces: number, rounding: Decimal.Rounding): string; | ||||
| 
 | ||||
|     toFraction(max_denominator?: Decimal.Value): Decimal[]; | ||||
| 
 | ||||
|     toHexadecimal(significantDigits?: number): string; | ||||
|     toHexadecimal(significantDigits: number, rounding: Decimal.Rounding): string; | ||||
|     toHex(significantDigits?: number): string; | ||||
|     toHex(significantDigits: number, rounding?: Decimal.Rounding): string; | ||||
| 
 | ||||
|     toJSON(): string; | ||||
| 
 | ||||
|     toNearest(n: Decimal.Value, rounding?: Decimal.Rounding): Decimal; | ||||
| 
 | ||||
|     toNumber(): number; | ||||
| 
 | ||||
|     toOctal(significantDigits?: number): string; | ||||
|     toOctal(significantDigits: number, rounding: Decimal.Rounding): string; | ||||
| 
 | ||||
|     toPower(n: Decimal.Value): Decimal; | ||||
|     pow(n: Decimal.Value): Decimal; | ||||
| 
 | ||||
|     toPrecision(significantDigits?: number): string; | ||||
|     toPrecision(significantDigits: number, rounding: Decimal.Rounding): string; | ||||
| 
 | ||||
|     toSignificantDigits(significantDigits?: number): Decimal; | ||||
|     toSignificantDigits(significantDigits: number, rounding: Decimal.Rounding): Decimal; | ||||
|     toSD(significantDigits?: number): Decimal; | ||||
|     toSD(significantDigits: number, rounding: Decimal.Rounding): Decimal; | ||||
| 
 | ||||
|     toString(): string; | ||||
| 
 | ||||
|     truncated(): Decimal; | ||||
|     trunc(): Decimal; | ||||
| 
 | ||||
|     valueOf(): string; | ||||
| 
 | ||||
|     static abs(n: Decimal.Value): Decimal; | ||||
|     static acos(n: Decimal.Value): Decimal; | ||||
|     static acosh(n: Decimal.Value): Decimal; | ||||
|     static add(x: Decimal.Value, y: Decimal.Value): Decimal; | ||||
|     static asin(n: Decimal.Value): Decimal; | ||||
|     static asinh(n: Decimal.Value): Decimal; | ||||
|     static atan(n: Decimal.Value): Decimal; | ||||
|     static atanh(n: Decimal.Value): Decimal; | ||||
|     static atan2(y: Decimal.Value, x: Decimal.Value): Decimal; | ||||
|     static cbrt(n: Decimal.Value): Decimal; | ||||
|     static ceil(n: Decimal.Value): Decimal; | ||||
|     static clamp(n: Decimal.Value, min: Decimal.Value, max: Decimal.Value): Decimal; | ||||
|     static clone(object?: Decimal.Config): Decimal.Constructor; | ||||
|     static config(object: Decimal.Config): Decimal.Constructor; | ||||
|     static cos(n: Decimal.Value): Decimal; | ||||
|     static cosh(n: Decimal.Value): Decimal; | ||||
|     static div(x: Decimal.Value, y: Decimal.Value): Decimal; | ||||
|     static exp(n: Decimal.Value): Decimal; | ||||
|     static floor(n: Decimal.Value): Decimal; | ||||
|     static hypot(...n: Decimal.Value[]): Decimal; | ||||
|     static isDecimal(object: any): object is Decimal; | ||||
|     static ln(n: Decimal.Value): Decimal; | ||||
|     static log(n: Decimal.Value, base?: Decimal.Value): Decimal; | ||||
|     static log2(n: Decimal.Value): Decimal; | ||||
|     static log10(n: Decimal.Value): Decimal; | ||||
|     static max(...n: Decimal.Value[]): Decimal; | ||||
|     static min(...n: Decimal.Value[]): Decimal; | ||||
|     static mod(x: Decimal.Value, y: Decimal.Value): Decimal; | ||||
|     static mul(x: Decimal.Value, y: Decimal.Value): Decimal; | ||||
|     static noConflict(): Decimal.Constructor;   // Browser only
 | ||||
|     static pow(base: Decimal.Value, exponent: Decimal.Value): Decimal; | ||||
|     static random(significantDigits?: number): Decimal; | ||||
|     static round(n: Decimal.Value): Decimal; | ||||
|     static set(object: Decimal.Config): Decimal.Constructor; | ||||
|     static sign(n: Decimal.Value): number; | ||||
|     static sin(n: Decimal.Value): Decimal; | ||||
|     static sinh(n: Decimal.Value): Decimal; | ||||
|     static sqrt(n: Decimal.Value): Decimal; | ||||
|     static sub(x: Decimal.Value, y: Decimal.Value): Decimal; | ||||
|     static sum(...n: Decimal.Value[]): Decimal; | ||||
|     static tan(n: Decimal.Value): Decimal; | ||||
|     static tanh(n: Decimal.Value): Decimal; | ||||
|     static trunc(n: Decimal.Value): Decimal; | ||||
| 
 | ||||
|     static readonly default?: Decimal.Constructor; | ||||
|     static readonly Decimal?: Decimal.Constructor; | ||||
| 
 | ||||
|     static readonly precision: number; | ||||
|     static readonly rounding: Decimal.Rounding; | ||||
|     static readonly toExpNeg: number; | ||||
|     static readonly toExpPos: number; | ||||
|     static readonly minE: number; | ||||
|     static readonly maxE: number; | ||||
|     static readonly crypto: boolean; | ||||
|     static readonly modulo: Decimal.Modulo; | ||||
| 
 | ||||
|     static readonly ROUND_UP: 0; | ||||
|     static readonly ROUND_DOWN: 1; | ||||
|     static readonly ROUND_CEIL: 2; | ||||
|     static readonly ROUND_FLOOR: 3; | ||||
|     static readonly ROUND_HALF_UP: 4; | ||||
|     static readonly ROUND_HALF_DOWN: 5; | ||||
|     static readonly ROUND_HALF_EVEN: 6; | ||||
|     static readonly ROUND_HALF_CEIL: 7; | ||||
|     static readonly ROUND_HALF_FLOOR: 8; | ||||
|     static readonly EUCLID: 9; | ||||
| } | ||||
| 
 | ||||
| declare type Exact<A, W> = (A extends unknown ? (W extends A ? { | ||||
|     [K in keyof A]: Exact<A[K], W[K]>; | ||||
| } : W) : never) | (A extends Narrowable ? A : never); | ||||
| 
 | ||||
| export declare function getRuntime(): GetRuntimeOutput; | ||||
| 
 | ||||
| declare type GetRuntimeOutput = { | ||||
|     id: RuntimeName; | ||||
|     prettyName: string; | ||||
|     isEdge: boolean; | ||||
| }; | ||||
| 
 | ||||
| declare class JsonNull extends NullTypesEnumValue { | ||||
|     #private; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Generates more strict variant of an enum which, unlike regular enum, | ||||
|  * throws on non-existing property access. This can be useful in following situations: | ||||
|  * - we have an API, that accepts both `undefined` and `SomeEnumType` as an input | ||||
|  * - enum values are generated dynamically from DMMF. | ||||
|  * | ||||
|  * In that case, if using normal enums and no compile-time typechecking, using non-existing property | ||||
|  * will result in `undefined` value being used, which will be accepted. Using strict enum | ||||
|  * in this case will help to have a runtime exception, telling you that you are probably doing something wrong. | ||||
|  * | ||||
|  * Note: if you need to check for existence of a value in the enum you can still use either | ||||
|  * `in` operator or `hasOwnProperty` function. | ||||
|  * | ||||
|  * @param definition | ||||
|  * @returns | ||||
|  */ | ||||
| export declare function makeStrictEnum<T extends Record<PropertyKey, string | number>>(definition: T): T; | ||||
| 
 | ||||
| declare type Narrowable = string | number | bigint | boolean | []; | ||||
| 
 | ||||
| declare class NullTypesEnumValue extends ObjectEnumValue { | ||||
|     _getNamespace(): string; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Base class for unique values of object-valued enums. | ||||
|  */ | ||||
| declare abstract class ObjectEnumValue { | ||||
|     constructor(arg?: symbol); | ||||
|     abstract _getNamespace(): string; | ||||
|     _getName(): string; | ||||
|     toString(): string; | ||||
| } | ||||
| 
 | ||||
| export declare const objectEnumValues: { | ||||
|     classes: { | ||||
|         DbNull: typeof DbNull; | ||||
|         JsonNull: typeof JsonNull; | ||||
|         AnyNull: typeof AnyNull; | ||||
|     }; | ||||
|     instances: { | ||||
|         DbNull: DbNull; | ||||
|         JsonNull: JsonNull; | ||||
|         AnyNull: AnyNull; | ||||
|     }; | ||||
| }; | ||||
| 
 | ||||
| declare type Operation = 'findFirst' | 'findFirstOrThrow' | 'findUnique' | 'findUniqueOrThrow' | 'findMany' | 'create' | 'createMany' | 'createManyAndReturn' | 'update' | 'updateMany' | 'updateManyAndReturn' | 'upsert' | 'delete' | 'deleteMany' | 'aggregate' | 'count' | 'groupBy' | '$queryRaw' | '$executeRaw' | '$queryRawUnsafe' | '$executeRawUnsafe' | 'findRaw' | 'aggregateRaw' | '$runCommandRaw'; | ||||
| 
 | ||||
| declare namespace Public { | ||||
|     export { | ||||
|         validator | ||||
|     } | ||||
| } | ||||
| export { Public } | ||||
| 
 | ||||
| declare type RuntimeName = 'workerd' | 'deno' | 'netlify' | 'node' | 'bun' | 'edge-light' | ''; | ||||
| 
 | ||||
| declare function validator<V>(): <S>(select: Exact<S, V>) => S; | ||||
| 
 | ||||
| declare function validator<C, M extends Exclude<keyof C, `$${string}`>, O extends keyof C[M] & Operation>(client: C, model: M, operation: O): <S>(select: Exact<S, Args<C[M], O>>) => S; | ||||
| 
 | ||||
| declare function validator<C, M extends Exclude<keyof C, `$${string}`>, O extends keyof C[M] & Operation, P extends keyof Args<C[M], O>>(client: C, model: M, operation: O, prop: P): <S>(select: Exact<S, Args<C[M], O>[P]>) => S; | ||||
| 
 | ||||
| export { } | ||||
							
								
								
									
										16
									
								
								src/app/generated/prisma-client/runtime/index-browser.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/app/generated/prisma-client/runtime/index-browser.js
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										3647
									
								
								src/app/generated/prisma-client/runtime/library.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3647
									
								
								src/app/generated/prisma-client/runtime/library.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										146
									
								
								src/app/generated/prisma-client/runtime/library.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								src/app/generated/prisma-client/runtime/library.js
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										83
									
								
								src/app/generated/prisma-client/runtime/react-native.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								src/app/generated/prisma-client/runtime/react-native.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										35
									
								
								src/app/generated/prisma-client/runtime/wasm.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/app/generated/prisma-client/runtime/wasm.js
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										77
									
								
								src/app/generated/prisma-client/schema.prisma
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								src/app/generated/prisma-client/schema.prisma
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,77 @@ | ||||
| // This is your Prisma schema file, | ||||
| // learn more about it in the docs: https://pris.ly/d/prisma-schema | ||||
| 
 | ||||
| generator client { | ||||
|   provider      = "prisma-client-js" | ||||
|   binaryTargets = ["native", "debian-openssl-3.0.x"] | ||||
|   output        = "/app/generated/prisma-client" | ||||
| } | ||||
| 
 | ||||
| datasource db { | ||||
|   provider = "postgresql" | ||||
|   // NOTE: When using mysql or sqlserver, uncomment the @db.Text annotations in model Account below | ||||
|   // Further reading: | ||||
|   // https://next-auth.js.org/adapters/prisma#create-the-prisma-schema | ||||
|   // https://www.prisma.io/docs/reference/api-reference/prisma-schema-reference#string | ||||
|   url      = env("DATABASE_URL") | ||||
| } | ||||
| 
 | ||||
| // Necessary for Next auth | ||||
| model Account { | ||||
|   id                       String  @id @default(cuid()) | ||||
|   userId                   String | ||||
|   type                     String | ||||
|   provider                 String | ||||
|   providerAccountId        String | ||||
|   refresh_token            String? @db.Text | ||||
|   access_token             String? // @db.Text | ||||
|   expires_at               Int? | ||||
|   token_type               String? | ||||
|   scope                    String? | ||||
|   id_token                 String? // @db.Text | ||||
|   session_state            String? | ||||
|   user                     User    @relation(fields: [userId], references: [id], onDelete: Cascade) | ||||
|   refresh_token_expires_in Int? | ||||
| 
 | ||||
|   @@unique([provider, providerAccountId]) | ||||
| } | ||||
| 
 | ||||
| model Session { | ||||
|   id           String   @id @default(cuid()) | ||||
|   sessionToken String   @unique | ||||
|   userId       String | ||||
|   expires      DateTime | ||||
|   user         User     @relation(fields: [userId], references: [id], onDelete: Cascade) | ||||
| } | ||||
| 
 | ||||
| model User { | ||||
|   id            String    @id @default(cuid()) | ||||
|   name          String? | ||||
|   email         String?   @unique | ||||
|   emailVerified DateTime? | ||||
|   image         String? | ||||
|   accounts      Account[] | ||||
|   sessions      Session[] | ||||
|   files         File[] // Relation to the File model | ||||
| } | ||||
| 
 | ||||
| model VerificationToken { | ||||
|   identifier String | ||||
|   token      String   @unique | ||||
|   expires    DateTime | ||||
| 
 | ||||
|   @@unique([identifier, token]) | ||||
| } | ||||
| 
 | ||||
| model File { | ||||
|   id           String   @id @default(cuid()) | ||||
|   url          String | ||||
|   name         String | ||||
|   size         Int // Size in bytes | ||||
|   extension    String | ||||
|   uploadDate   DateTime @default(now()) | ||||
|   description  String   @default("") | ||||
|   uploadedBy   User?    @relation(fields: [uploadedById], references: [id], onDelete: SetNull) | ||||
|   uploadedById String? | ||||
|   public       Boolean  @default(false) // Indicates if the file is public or private | ||||
| } | ||||
							
								
								
									
										1
									
								
								src/app/generated/prisma-client/wasm.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/app/generated/prisma-client/wasm.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| export * from "./index" | ||||
							
								
								
									
										225
									
								
								src/app/generated/prisma-client/wasm.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										225
									
								
								src/app/generated/prisma-client/wasm.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,225 @@ | ||||
| 
 | ||||
| /* !!! This is code generated by Prisma. Do not edit directly. !!! | ||||
| /* eslint-disable */ | ||||
| 
 | ||||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||||
| 
 | ||||
| const { | ||||
|   Decimal, | ||||
|   objectEnumValues, | ||||
|   makeStrictEnum, | ||||
|   Public, | ||||
|   getRuntime, | ||||
|   skip | ||||
| } = require('./runtime/index-browser.js') | ||||
| 
 | ||||
| 
 | ||||
| const Prisma = {} | ||||
| 
 | ||||
| exports.Prisma = Prisma | ||||
| exports.$Enums = {} | ||||
| 
 | ||||
| /** | ||||
|  * Prisma Client JS version: 6.8.2 | ||||
|  * Query Engine version: 2060c79ba17c6bb9f5823312b6f6b7f4a845738e | ||||
|  */ | ||||
| Prisma.prismaVersion = { | ||||
|   client: "6.8.2", | ||||
|   engine: "2060c79ba17c6bb9f5823312b6f6b7f4a845738e" | ||||
| } | ||||
| 
 | ||||
| Prisma.PrismaClientKnownRequestError = () => { | ||||
|   const runtimeName = getRuntime().prettyName; | ||||
|   throw new Error(`PrismaClientKnownRequestError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
 | ||||
| In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
 | ||||
| )}; | ||||
| Prisma.PrismaClientUnknownRequestError = () => { | ||||
|   const runtimeName = getRuntime().prettyName; | ||||
|   throw new Error(`PrismaClientUnknownRequestError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
 | ||||
| In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
 | ||||
| )} | ||||
| Prisma.PrismaClientRustPanicError = () => { | ||||
|   const runtimeName = getRuntime().prettyName; | ||||
|   throw new Error(`PrismaClientRustPanicError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
 | ||||
| In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
 | ||||
| )} | ||||
| Prisma.PrismaClientInitializationError = () => { | ||||
|   const runtimeName = getRuntime().prettyName; | ||||
|   throw new Error(`PrismaClientInitializationError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
 | ||||
| In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
 | ||||
| )} | ||||
| Prisma.PrismaClientValidationError = () => { | ||||
|   const runtimeName = getRuntime().prettyName; | ||||
|   throw new Error(`PrismaClientValidationError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
 | ||||
| In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
 | ||||
| )} | ||||
| Prisma.Decimal = Decimal | ||||
| 
 | ||||
| /** | ||||
|  * Re-export of sql-template-tag | ||||
|  */ | ||||
| Prisma.sql = () => { | ||||
|   const runtimeName = getRuntime().prettyName; | ||||
|   throw new Error(`sqltag is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
 | ||||
| In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
 | ||||
| )} | ||||
| Prisma.empty = () => { | ||||
|   const runtimeName = getRuntime().prettyName; | ||||
|   throw new Error(`empty is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
 | ||||
| In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
 | ||||
| )} | ||||
| Prisma.join = () => { | ||||
|   const runtimeName = getRuntime().prettyName; | ||||
|   throw new Error(`join is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
 | ||||
| In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
 | ||||
| )} | ||||
| Prisma.raw = () => { | ||||
|   const runtimeName = getRuntime().prettyName; | ||||
|   throw new Error(`raw is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
 | ||||
| In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
 | ||||
| )} | ||||
| Prisma.validator = Public.validator | ||||
| 
 | ||||
| /** | ||||
| * Extensions | ||||
| */ | ||||
| Prisma.getExtensionContext = () => { | ||||
|   const runtimeName = getRuntime().prettyName; | ||||
|   throw new Error(`Extensions.getExtensionContext is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
 | ||||
| In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
 | ||||
| )} | ||||
| Prisma.defineExtension = () => { | ||||
|   const runtimeName = getRuntime().prettyName; | ||||
|   throw new Error(`Extensions.defineExtension is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
 | ||||
| In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
 | ||||
| )} | ||||
| 
 | ||||
| /** | ||||
|  * Shorthand utilities for JSON filtering | ||||
|  */ | ||||
| Prisma.DbNull = objectEnumValues.instances.DbNull | ||||
| Prisma.JsonNull = objectEnumValues.instances.JsonNull | ||||
| Prisma.AnyNull = objectEnumValues.instances.AnyNull | ||||
| 
 | ||||
| Prisma.NullTypes = { | ||||
|   DbNull: objectEnumValues.classes.DbNull, | ||||
|   JsonNull: objectEnumValues.classes.JsonNull, | ||||
|   AnyNull: objectEnumValues.classes.AnyNull | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * Enums | ||||
|  */ | ||||
| 
 | ||||
| exports.Prisma.TransactionIsolationLevel = makeStrictEnum({ | ||||
|   ReadUncommitted: 'ReadUncommitted', | ||||
|   ReadCommitted: 'ReadCommitted', | ||||
|   RepeatableRead: 'RepeatableRead', | ||||
|   Serializable: 'Serializable' | ||||
| }); | ||||
| 
 | ||||
| exports.Prisma.AccountScalarFieldEnum = { | ||||
|   id: 'id', | ||||
|   userId: 'userId', | ||||
|   type: 'type', | ||||
|   provider: 'provider', | ||||
|   providerAccountId: 'providerAccountId', | ||||
|   refresh_token: 'refresh_token', | ||||
|   access_token: 'access_token', | ||||
|   expires_at: 'expires_at', | ||||
|   token_type: 'token_type', | ||||
|   scope: 'scope', | ||||
|   id_token: 'id_token', | ||||
|   session_state: 'session_state', | ||||
|   refresh_token_expires_in: 'refresh_token_expires_in' | ||||
| }; | ||||
| 
 | ||||
| exports.Prisma.SessionScalarFieldEnum = { | ||||
|   id: 'id', | ||||
|   sessionToken: 'sessionToken', | ||||
|   userId: 'userId', | ||||
|   expires: 'expires' | ||||
| }; | ||||
| 
 | ||||
| exports.Prisma.UserScalarFieldEnum = { | ||||
|   id: 'id', | ||||
|   name: 'name', | ||||
|   email: 'email', | ||||
|   emailVerified: 'emailVerified', | ||||
|   image: 'image' | ||||
| }; | ||||
| 
 | ||||
| exports.Prisma.VerificationTokenScalarFieldEnum = { | ||||
|   identifier: 'identifier', | ||||
|   token: 'token', | ||||
|   expires: 'expires' | ||||
| }; | ||||
| 
 | ||||
| exports.Prisma.FileScalarFieldEnum = { | ||||
|   id: 'id', | ||||
|   url: 'url', | ||||
|   name: 'name', | ||||
|   size: 'size', | ||||
|   extension: 'extension', | ||||
|   uploadDate: 'uploadDate', | ||||
|   description: 'description', | ||||
|   uploadedById: 'uploadedById', | ||||
|   public: 'public' | ||||
| }; | ||||
| 
 | ||||
| exports.Prisma.SortOrder = { | ||||
|   asc: 'asc', | ||||
|   desc: 'desc' | ||||
| }; | ||||
| 
 | ||||
| exports.Prisma.QueryMode = { | ||||
|   default: 'default', | ||||
|   insensitive: 'insensitive' | ||||
| }; | ||||
| 
 | ||||
| exports.Prisma.NullsOrder = { | ||||
|   first: 'first', | ||||
|   last: 'last' | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| exports.Prisma.ModelName = { | ||||
|   Account: 'Account', | ||||
|   Session: 'Session', | ||||
|   User: 'User', | ||||
|   VerificationToken: 'VerificationToken', | ||||
|   File: 'File' | ||||
| }; | ||||
| 
 | ||||
| /** | ||||
|  * This is a stub Prisma Client that will error at runtime if called. | ||||
|  */ | ||||
| class PrismaClient { | ||||
|   constructor() { | ||||
|     return new Proxy(this, { | ||||
|       get(target, prop) { | ||||
|         let message | ||||
|         const runtime = getRuntime() | ||||
|         if (runtime.isEdge) { | ||||
|           message = `PrismaClient is not configured to run in ${runtime.prettyName}. In order to run Prisma Client on edge runtime, either:
 | ||||
| - Use Prisma Accelerate: https://pris.ly/d/accelerate
 | ||||
| - Use Driver Adapters: https://pris.ly/d/driver-adapters
 | ||||
| `;
 | ||||
|         } else { | ||||
|           message = 'PrismaClient is unable to run in this browser environment, or has been bundled for the browser (running in `' + runtime.prettyName + '`).' | ||||
|         } | ||||
| 
 | ||||
|         message += ` | ||||
| If this is unexpected, please open an issue: https://pris.ly/prisma-prisma-bug-report`
 | ||||
| 
 | ||||
|         throw new Error(message) | ||||
|       } | ||||
|     }) | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| exports.PrismaClient = PrismaClient | ||||
| 
 | ||||
| Object.assign(exports, Prisma) | ||||
							
								
								
									
										6
									
								
								src/app/loading.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/app/loading.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | ||||
| import LoadingSkeleton from './LoadingSkeleton'; | ||||
| 
 | ||||
| export default function Loading() { | ||||
|   // You can add any UI inside Loading, including a Skeleton.
 | ||||
|   return <LoadingSkeleton /> | ||||
| } | ||||
							
								
								
									
										101
									
								
								src/app/page.tsx
									
									
									
									
									
								
							
							
						
						
									
										101
									
								
								src/app/page.tsx
									
									
									
									
									
								
							| @ -1,15 +1,53 @@ | ||||
| "use client"; | ||||
| 
 | ||||
| import Link from "next/link"; | ||||
| import { auth } from "~/server/auth"; | ||||
| import { HydrateClient } from "~/trpc/server"; | ||||
| import { useEffect, useState } from "react"; | ||||
| import FileGrid from "~/app/_components/FileGrid"; | ||||
| import UploadForm from "~/app/_components/UploadForm"; | ||||
| import { Toaster } from "react-hot-toast"; | ||||
| import { Suspense } from "react"; | ||||
| import LoadingSkeleton from "./LoadingSkeleton"; | ||||
| 
 | ||||
| export default async function Home() { | ||||
|   const session = await auth(); | ||||
| // Custom fallback for FileGrid
 | ||||
| function FileGridFallback() { | ||||
|   return ( | ||||
|     <div className="grid w-full max-w-4xl animate-pulse grid-cols-1 gap-6 sm:grid-cols-2 md:grid-cols-3"> | ||||
|       {[...Array(6)].map((_, i) => ( | ||||
|         <div key={i} className="flex flex-col items-center"> | ||||
|           <span className="mb-2 text-lg text-white/60">Loading</span> | ||||
|           <div className="h-32 w-full rounded bg-white/10" /> | ||||
|         </div> | ||||
|       ))} | ||||
|     </div> | ||||
|   ); | ||||
| } | ||||
| 
 | ||||
| // Custom fallback for UploadForm
 | ||||
| function UploadFormFallback() { | ||||
|   return ( | ||||
|     <div className="mt-8 flex w-full max-w-md animate-pulse flex-col gap-4"> | ||||
|       <div className="h-10 rounded bg-white/20" /> | ||||
|       <div className="h-10 rounded bg-white/10" /> | ||||
|     </div> | ||||
|   ); | ||||
| } | ||||
| 
 | ||||
| function Home() { | ||||
|   const [session, setSession] = useState<{ user?: any } | null>(null); | ||||
|   const [loading, setLoading] = useState(true); | ||||
|   useEffect(() => { | ||||
|     async function fetchSession() { | ||||
|       setLoading(true); | ||||
|       const res = await fetch("/api/auth/session"); | ||||
|       const data = await res.json(); | ||||
|       setSession(data); | ||||
|       setLoading(false); | ||||
|     } | ||||
|     fetchSession(); | ||||
|   }, []); | ||||
| 
 | ||||
|   return ( | ||||
|     <HydrateClient> | ||||
|     <> | ||||
|       <Toaster position="top-right" reverseOrder={false} /> | ||||
|       <main className="relative flex min-h-screen flex-col items-center justify-center bg-gradient-to-b from-[#2e026d] to-[#15162c] text-white"> | ||||
|         {/* Top-right corner sign-out button */} | ||||
| @ -53,28 +91,59 @@ export default async function Home() { | ||||
|           {/* Conditionally render FileGrid and UploadForm if the user is logged in */} | ||||
|           {session?.user ? ( | ||||
|             <> | ||||
|               <FileGrid session={session} /> | ||||
|               <UploadForm /> | ||||
|               <Suspense fallback={<FileGridFallback />}> | ||||
|                 <FileGrid session={session as { user: { id: string } }} /> | ||||
|               </Suspense> | ||||
|               <Suspense fallback={<UploadFormFallback />}> | ||||
|                 <UploadForm /> | ||||
|               </Suspense> | ||||
|             </> | ||||
|           ) : ( | ||||
|           ) : !loading ? ( | ||||
|             <p className="text-center text-2xl text-white"> | ||||
|               Please log in to upload and view files. | ||||
|             </p> | ||||
|           )} | ||||
|           ) : null} | ||||
|           {!session?.user && ( | ||||
|             <div className="flex flex-col items-center gap-2"> | ||||
|               <div className="flex flex-col items-center justify-center gap-4"> | ||||
|                 <Link | ||||
|                   href={session ? "/api/auth/signout" : "/api/auth/signin"} | ||||
|                   className="rounded-full bg-white/10 px-10 py-3 font-semibold no-underline transition hover:bg-white/20" | ||||
|                 > | ||||
|                   {session ? "Sign out" : "Sign in"} | ||||
|                 </Link> | ||||
|                 {!loading ? ( | ||||
|                   <Link | ||||
|                     href={session ? "/api/auth/signout" : "/api/auth/signin"} | ||||
|                     className="rounded-full bg-white/10 px-10 py-3 font-semibold no-underline transition hover:bg-white/20" | ||||
|                   > | ||||
|                     {session ? "Sign out" : "Sign in"} | ||||
|                   </Link> | ||||
|                 ) : ( | ||||
|                   <div className="flex h-10 items-center justify-center"> | ||||
|                     <svg | ||||
|                       className="h-6 w-6 animate-spin text-white/70" | ||||
|                       xmlns="http://www.w3.org/2000/svg" | ||||
|                       fill="none" | ||||
|                       viewBox="0 0 24 24" | ||||
|                     > | ||||
|                       <circle | ||||
|                         className="opacity-25" | ||||
|                         cx="12" | ||||
|                         cy="12" | ||||
|                         r="10" | ||||
|                         stroke="currentColor" | ||||
|                         strokeWidth="4" | ||||
|                       /> | ||||
|                       <path | ||||
|                         className="opacity-75" | ||||
|                         fill="currentColor" | ||||
|                         d="M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z" | ||||
|                       /> | ||||
|                     </svg> | ||||
|                   </div> | ||||
|                 )} | ||||
|               </div> | ||||
|             </div> | ||||
|           )} | ||||
|         </div> | ||||
|       </main> | ||||
|     </HydrateClient> | ||||
|     </> | ||||
|   ); | ||||
| } | ||||
| 
 | ||||
| export default Home; | ||||
|  | ||||
| @ -91,7 +91,7 @@ export default function SearchFile() { | ||||
|                 className="flex place-content-end w-xxs flex-col gap-4 rounded-xl bg-white/10 p-4 hover:bg-white/20" | ||||
|               > | ||||
|                 <div className="self-center max-w-100 sm:max-w-50"> | ||||
|                   <FilePreview fileId={file.id} fileType={file.extension} /> | ||||
|                   <FilePreview fileId={file.id} fileType={file.extension} share={false} /> | ||||
|                 </div> | ||||
| 
 | ||||
|                 <button onClick={() => router.push(pageUrl + file.url)}> | ||||
|  | ||||
							
								
								
									
										70
									
								
								src/app/share/LoadingSkeleton.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								src/app/share/LoadingSkeleton.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,70 @@ | ||||
| import React, { Suspense } from "react"; | ||||
| import { HomeButton } from "~/app/_components/HomeButton"; // Import the client component
 | ||||
| import { Toaster } from "react-hot-toast"; | ||||
| import { | ||||
|   FileActionsContainer, | ||||
| } from "~/app/_components/ActionButtons"; // Import the client component
 | ||||
| 
 | ||||
| const LoadingSkeleton: React.FC = () => ( | ||||
|     <main className="flex min-h-screen flex-col items-center justify-center bg-gradient-to-b from-[#2e026d] to-[#15162c] text-white"> | ||||
|       <div className="absolute top-4 left-4"> | ||||
|          <HomeButton /> | ||||
|       </div> | ||||
|       <Toaster position="top-right" reverseOrder={false} /> | ||||
|       <div className="container flex flex-col items-center gap-12 px-4 py-16"> | ||||
|         <h1 className="text-5xl font-extrabold tracking-tight sm:text-[5rem]"> | ||||
|           <span className="text-[hsl(280,100%,70%)]">File</span> Details | ||||
|         </h1> | ||||
|         <div className="mt-6"> | ||||
|           <svg | ||||
|                       className="h-6 w-6 animate-spin text-white/70" | ||||
|                       xmlns="http://www.w3.org/2000/svg" | ||||
|                       fill="none" | ||||
|                       viewBox="0 0 24 24" | ||||
|                     > | ||||
|                       <circle | ||||
|                         className="opacity-25" | ||||
|                         cx="12" | ||||
|                         cy="12" | ||||
|                         r="10" | ||||
|                         stroke="currentColor" | ||||
|                         strokeWidth="4" | ||||
|                       /> | ||||
|                       <path | ||||
|                         className="opacity-75" | ||||
|                         fill="currentColor" | ||||
|                         d="M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z" | ||||
|                       /> | ||||
|                     </svg> | ||||
|         </div> | ||||
|         <div className="w-full max-w-md rounded-lg bg-white/10 p-6 text-white shadow-md"> | ||||
|           <p> | ||||
|             <strong>Name:</strong> <span className="inline-block h-6 w-24 rounded bg-white/20 animate-pulse align-middle ml-2" /> | ||||
|           </p> | ||||
|           <p> | ||||
|             <strong>Size:</strong> <span className="inline-block h-6 w-16 rounded bg-white/20 animate-pulse align-middle ml-2" /> | ||||
|           </p> | ||||
|           <p> | ||||
|             <strong>Owner:</strong> <span className="inline-block h-6 w-20 rounded bg-white/20 animate-pulse align-middle ml-2" /> | ||||
|           </p> | ||||
|           <p> | ||||
|             <strong>Upload Date:</strong> <span className="inline-block h-6 w-28 rounded bg-white/20 animate-pulse align-middle ml-2" /> | ||||
|           </p> | ||||
|           <div> | ||||
|             <strong>Description:</strong> <span className="inline-block h-6 w-40 rounded bg-white/20 animate-pulse align-middle ml-2" /> | ||||
|           </div> | ||||
|           <div className="mt-4 flex justify-center"> | ||||
|             <FileActionsContainer | ||||
|               fileId={""} | ||||
|               fileName={""} | ||||
|               fileUrl={""} | ||||
|               isOwner={false} | ||||
|               isPublic={false} | ||||
|             /> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|     </main> | ||||
| ); | ||||
| 
 | ||||
| export default LoadingSkeleton; | ||||
							
								
								
									
										6
									
								
								src/app/share/loading.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/app/share/loading.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | ||||
| import LoadingSkeleton from './LoadingSkeleton'; | ||||
| 
 | ||||
| export default function Loading() { | ||||
|   // You can add any UI inside Loading, including a Skeleton.
 | ||||
|   return <LoadingSkeleton /> | ||||
| } | ||||
| @ -1,4 +1,5 @@ | ||||
| import { notFound } from "next/navigation"; | ||||
| import { Suspense } from "react"; | ||||
| import { FilePreview } from "~/app/_components/FilePreview"; | ||||
| import { HomeButton } from "~/app/_components/HomeButton"; // Import the client component
 | ||||
| import { Toaster } from "react-hot-toast"; | ||||
| @ -129,7 +130,9 @@ export default async function FilePreviewContainer({ | ||||
|         </h1> | ||||
|         <div className="mt-6"> | ||||
|           {fileDetails.type !== "unknown" && ( | ||||
|             <FilePreview fileId={fileDetails.id} fileType={fileDetails.type} /> | ||||
|             <Suspense fallback={<div className="text-white">Loading...</div>}> | ||||
|               <FilePreview fileId={fileDetails.id} fileType={fileDetails.type} share={true} /> | ||||
|             </Suspense> | ||||
|           )} | ||||
|         </div> | ||||
|         <div className="w-full max-w-md rounded-lg bg-white/10 p-6 text-white shadow-md"> | ||||
| @ -148,32 +151,40 @@ export default async function FilePreviewContainer({ | ||||
|           </p> | ||||
|           <p> | ||||
|             <strong>Owner:</strong>{" "} | ||||
|             <img | ||||
|             <Suspense fallback={<div className="text-white">Loading...</div>}> | ||||
|               <img | ||||
|               className="inline size-5 rounded-md" | ||||
|               src={fileDetails.ownerAvatar || ""} | ||||
|               alt="Owner avatar" | ||||
|             />{" "} | ||||
|               />{" "} | ||||
|             {fileDetails.owner} | ||||
|             </Suspense> | ||||
|           </p> | ||||
|           <p> | ||||
|             <strong>Upload Date:</strong>{" "} | ||||
|             {new Date(fileDetails.uploadDate).toLocaleString()} | ||||
|             <Suspense fallback={<div className="text-white">Loading...</div>}> | ||||
|               {new Date(fileDetails.uploadDate).toLocaleString()} | ||||
|             </Suspense> | ||||
|           </p> | ||||
|           <div> | ||||
|             <strong>Description:</strong>{" "} | ||||
|             <FileDescriptionContainer | ||||
|               fileId={fileDetails.id} | ||||
|               fileDescription={fileDetails.description} | ||||
|             /> | ||||
|             <Suspense fallback={<div className="text-white">Loading...</div>}> | ||||
|               <FileDescriptionContainer | ||||
|                 fileId={fileDetails.id} | ||||
|                 fileDescription={fileDetails.description} | ||||
|               /> | ||||
|             </Suspense> | ||||
|           </div> | ||||
|           <div className="mt-4 flex justify-center"> | ||||
|             <FileActionsContainer | ||||
|               fileId={fileDetails.id} | ||||
|               fileName={fileDetails.name} | ||||
|               fileUrl={fileDetails.url} | ||||
|               isOwner={session?.user?.id ? await checkOwner(fileDetails.ownerId, session.user.id) : false} | ||||
|               isPublic={fileDetails.isPublic} | ||||
|             /> | ||||
|             <Suspense fallback={<div className="text-white">Loading...</div>}> | ||||
|               <FileActionsContainer | ||||
|                 fileId={fileDetails.id} | ||||
|                 fileName={fileDetails.name} | ||||
|                 fileUrl={fileDetails.url} | ||||
|                 isOwner={session?.user?.id ? await checkOwner(fileDetails.ownerId, session.user.id) : false} | ||||
|                 isPublic={fileDetails.isPublic} | ||||
|               /> | ||||
|             </Suspense> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|  | ||||
							
								
								
									
										9
									
								
								src/app/styles/custom.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/app/styles/custom.css
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | ||||
| .markdown-body ul, | ||||
| .markdown-body ol { | ||||
|   list-style: initial; /* Ensures bullets or numbers are displayed */ | ||||
|   margin-left: 1.5em; /* Adds proper indentation */ | ||||
| } | ||||
| 
 | ||||
| .markdown-body li { | ||||
|   margin-bottom: 0.5em; /* Adds spacing between list items */ | ||||
| } | ||||
							
								
								
									
										78
									
								
								src/components/MarkdownRenderer.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								src/components/MarkdownRenderer.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,78 @@ | ||||
| import { useEffect } from "react"; | ||||
| import "github-markdown-css/github-markdown.css"; | ||||
| 
 | ||||
| interface MarkdownRendererProps { | ||||
|   markdownContent: string; | ||||
| } | ||||
| 
 | ||||
| export function MarkdownRenderer({ markdownContent }: MarkdownRendererProps) { | ||||
|   useEffect(() => { | ||||
|     if (markdownContent) { | ||||
|       const markdownContainer = document.querySelector("#markdown-preview"); | ||||
|       if (!markdownContainer) return; | ||||
| 
 | ||||
|       const codeBlocks = markdownContainer.querySelectorAll("code"); | ||||
| 
 | ||||
|       codeBlocks.forEach((block) => { | ||||
|         // Check if the block is already wrapped
 | ||||
|         if (block.parentElement?.classList.contains("code-wrapper")) return; | ||||
| 
 | ||||
|         // Check if the code block is multiline
 | ||||
|         const isMultiline = block.textContent?.includes("\n"); | ||||
|         if (!isMultiline) return; | ||||
| 
 | ||||
|         // Create a wrapper only if it doesn't already exist
 | ||||
|         const wrapper = document.createElement("div"); | ||||
|         wrapper.className = "code-wrapper"; // Add a class to identify the wrapper
 | ||||
|         wrapper.style.display = "flex"; | ||||
|         wrapper.style.alignItems = "flex-start"; | ||||
|         wrapper.style.justifyContent = "space-between"; | ||||
|         wrapper.style.gap = "8px"; | ||||
|         wrapper.style.width = "100%"; | ||||
|         wrapper.style.position = "relative"; | ||||
| 
 | ||||
|         const codeContainer = document.createElement("div"); | ||||
|         codeContainer.style.flex = "1"; | ||||
|         codeContainer.appendChild(block.cloneNode(true)); | ||||
| 
 | ||||
|         const button = document.createElement("button"); | ||||
|         button.innerHTML = ` | ||||
|           <img src="/icons/copy.svg" alt="Copy" class="h-4 w-4" style="filter: invert(1) sepia(1) saturate(5) hue-rotate(180deg);"/> | ||||
|         `;
 | ||||
|         button.className = | ||||
|           "copy-button inline-flex items-center justify-center bg-gray-200 rounded hover:bg-gray-300 dark:bg-gray-700 dark:text-gray-200 dark:hover:bg-gray-600 transition"; | ||||
|         button.style.marginLeft = "8px"; | ||||
|         button.style.width = "1.5rem"; | ||||
|         button.style.height = "1.5rem"; | ||||
| 
 | ||||
|         button.addEventListener("click", () => { | ||||
|           navigator.clipboard.writeText(block.textContent || "").then(() => { | ||||
|             button.innerHTML = ` | ||||
|               <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | ||||
|                 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" /> | ||||
|               </svg> | ||||
|             `;
 | ||||
|             setTimeout(() => { | ||||
|               button.innerHTML = ` | ||||
|                 <img src="/icons/copy.svg" alt="Copy" class="h-4 w-4" style="filter: invert(1) sepia(1) saturate(5) hue-rotate(180deg);"/> | ||||
|               `;
 | ||||
|             }, 2000); | ||||
|           }); | ||||
|         }); | ||||
| 
 | ||||
|         wrapper.appendChild(codeContainer); | ||||
|         wrapper.appendChild(button); | ||||
| 
 | ||||
|         // Replace the original block with the wrapper
 | ||||
|         block.replaceWith(wrapper); | ||||
|       }); | ||||
|     } | ||||
|   }, [markdownContent]); | ||||
| 
 | ||||
|   return ( | ||||
|     <div className="markdown-body max-w-full p-4 pt-0 pb-0" id="markdown-preview"> | ||||
|       <div dangerouslySetInnerHTML={{ __html: markdownContent }} /> | ||||
|     </div> | ||||
|   ); | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										0
									
								
								src/server/api/routers/file.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								src/server/api/routers/file.ts
									
									
									
									
									
										Normal file
									
								
							| @ -1,4 +1,5 @@ | ||||
| import { PrismaClient } from "@prisma/client"; | ||||
| import { PrismaClient } from "~/app/generated/prisma-client"; | ||||
| import { withAccelerate } from '@prisma/extension-accelerate' | ||||
| 
 | ||||
| import { env } from "~/env"; | ||||
| 
 | ||||
| @ -6,7 +7,7 @@ const createPrismaClient = () => | ||||
|   new PrismaClient({ | ||||
|     log: | ||||
|       env.NODE_ENV === "development" ? ["query", "error", "warn"] : ["error"], | ||||
|   }); | ||||
|   }).$extends(withAccelerate()); | ||||
| 
 | ||||
| const globalForPrisma = globalThis as unknown as { | ||||
|   prisma: ReturnType<typeof createPrismaClient> | undefined; | ||||
|  | ||||
| @ -1,32 +1,48 @@ | ||||
| // This function takes a file name as input and returns the file type based on its extension.
 | ||||
| import mime from "mime-types"; | ||||
| 
 | ||||
| export function getFileType(fileName: string): string { | ||||
|     const extension = fileName.split(".").pop()?.toLowerCase(); | ||||
|     const fileTypes: Record<string, string> = { | ||||
|         // Video
 | ||||
|         "mp4": "video/mp4", | ||||
|         "webm": "video/webm", | ||||
|         "ogg": "video/ogg", | ||||
|         // Image
 | ||||
|         "jpg": "image/jpeg", | ||||
|         "jpeg": "image/jpeg", | ||||
|         "png": "image/png", | ||||
|         "gif": "image/gif", | ||||
|         "svg": "image/svg+xml", | ||||
|         // Audio
 | ||||
|         "mp3": "audio/mpeg", | ||||
|         "wav": "audio/wav", | ||||
|         // Archive
 | ||||
|         "zip": "archive/zip", | ||||
|         "rar": "archive/rar", | ||||
|         "jar": "archive/jar", | ||||
|         "iso": "archive/iso", | ||||
|         // Text
 | ||||
|         "pdf": "text/pdf", | ||||
|         "txt": "text/plain", | ||||
|         // Code
 | ||||
|         "c": "code/c", | ||||
|         "cpp": "code/cpp", | ||||
|         "py": "code/python", | ||||
|         "js": "code/javascript", | ||||
|         "html": "code/html", | ||||
|         "css": "code/css", | ||||
|         "md": "markdown/markdown", | ||||
|         "json": "code/json", | ||||
|         "xml": "code/xml", | ||||
|         "csv": "code/csv", | ||||
|         // Markdown
 | ||||
|         "md": "markdown/markdown", | ||||
|         // Applications
 | ||||
|         "exe": "application/executable", | ||||
|         "apk": "application/android", | ||||
|     }; | ||||
|     return extension ? fileTypes[extension] || "unknown" : "unknown"; | ||||
|     return extension ? fileTypes[extension] || | ||||
|     //get the file type using the mime type library
 | ||||
|     mime.lookup(extension) || "application/octet-stream" : "application/octet-stream"; | ||||
| 
 | ||||
|   }; | ||||
| @ -34,8 +34,8 @@ | ||||
|     "next-env.d.ts", | ||||
|     "**/*.ts", | ||||
|     "**/*.tsx", | ||||
|     "**/*.cjs", | ||||
|     "**/*.js", | ||||
|     // "**/*.cjs", | ||||
|     // "**/*.js", | ||||
|     ".next/types/**/*.ts" | ||||
|   ], | ||||
|   "exclude": ["node_modules"] | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user