diff --git a/package.json b/package.json index 51366ad..1b0a8e5 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,9 @@ "vite": "^5.0.3" }, "dependencies": { + "lucide-svelte": "^0.462.0", + "multer": "^1.4.5-lts.1", + "svelte-radix": "^2.0.1" "@prisma/client": "^5.22.0", "@types/node": "^22.10.1", "argon2": "^0.41.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 86b24dc..8cc1f9b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,12 @@ importers: .: dependencies: + lucide-svelte: + specifier: ^0.462.0 + version: 0.462.0(svelte@5.2.7) + multer: + specifier: ^1.4.5-lts.1 + version: 1.4.5-lts.1 '@prisma/client': specifier: ^5.22.0 version: 5.22.0(prisma@5.22.0) @@ -664,6 +670,9 @@ packages: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} + append-field@1.0.0: + resolution: {integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==} + arg@5.0.2: resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} @@ -719,6 +728,13 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + busboy@1.6.0: + resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} + engines: {node: '>=10.16.0'} + callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} @@ -779,10 +795,17 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + concat-stream@1.6.2: + resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==} + engines: {'0': node >= 0.8} + cookie@0.6.0: resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} engines: {node: '>= 0.6'} + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -1053,6 +1076,7 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + is-arrayish@0.3.2: resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} @@ -1083,6 +1107,9 @@ packages: is-reference@3.0.3: resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} + + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} @@ -1155,6 +1182,11 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lucide-svelte@0.462.0: + resolution: {integrity: sha512-BTY44UyXEhlakuPMS4w7NayKhDYARzmhEv3E2YchTiNZ/aySS0F2ktPscTlXRgSZ9xwqoozhnhO1oKhm/nnmqg==} + peerDependencies: + svelte: ^3 || ^4 || ^5.0.0-next.42 + magic-string@0.30.13: resolution: {integrity: sha512-8rYBO+MsWkgjDSOvLomYnzhdwEG51olQ4zL5KXnNJWV5MNmrb4rTZdrtkhxjnD/QyZUqR/Z/XDsUs/4ej2nx0g==} @@ -1163,6 +1195,10 @@ packages: peerDependencies: svelte: ^3.56.0 || ^4.0.0 || ^5.0.0-next.120 + media-typer@0.3.0: + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + engines: {node: '>= 0.6'} + merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -1171,6 +1207,14 @@ packages: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -1178,10 +1222,17 @@ packages: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + minipass@7.1.2: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} + mkdirp@0.5.6: + resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} + hasBin: true + mri@1.2.0: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} @@ -1193,6 +1244,10 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + multer@1.4.5-lts.1: + resolution: {integrity: sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==} + engines: {node: '>= 6.0.0'} + mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} @@ -1435,6 +1490,9 @@ packages: resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==} engines: {node: '>=6'} + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -1445,6 +1503,8 @@ packages: read-cache@1.0.0: resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} @@ -1484,6 +1544,8 @@ packages: resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} engines: {node: '>=6'} + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} @@ -1522,6 +1584,9 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + streamsearch@1.1.0: + resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} + engines: {node: '>=10.0.0'} stack-trace@0.0.10: resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} @@ -1533,6 +1598,8 @@ packages: resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} engines: {node: '>=12'} + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} @@ -1646,6 +1713,13 @@ packages: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} + type-is@1.6.18: + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + engines: {node: '>= 0.6'} + + typedarray@0.0.6: + resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} + typescript-eslint@8.15.0: resolution: {integrity: sha512-wY4FRGl0ZI+ZU4Jo/yjdBu0lVTSML58pu6PgGtJmCufvzfV565pUF6iACQt092uFOd49iLOTX/sEVmHtbSrS+w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1746,6 +1820,9 @@ packages: resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} engines: {node: '>=12'} + xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} @@ -2257,6 +2334,8 @@ snapshots: normalize-path: 3.0.0 picomatch: 2.3.1 + append-field@1.0.0: {} + arg@5.0.2: {} argon2@0.41.1: @@ -2314,6 +2393,12 @@ snapshots: node-releases: 2.0.18 update-browserslist-db: 1.1.1(browserslist@4.24.2) + buffer-from@1.1.2: {} + + busboy@1.6.0: + dependencies: + streamsearch: 1.1.0 + callsites@3.1.0: {} camelcase-css@2.0.1: {} @@ -2376,8 +2461,17 @@ snapshots: concat-map@0.0.1: {} + concat-stream@1.6.2: + dependencies: + buffer-from: 1.1.2 + inherits: 2.0.4 + readable-stream: 2.3.8 + typedarray: 0.0.6 + cookie@0.6.0: {} + core-util-is@1.0.3: {} + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -2688,6 +2782,7 @@ snapshots: dependencies: '@types/estree': 1.0.6 + isarray@1.0.0: {} is-stream@2.0.1: {} isexe@2.0.0: {} @@ -2750,6 +2845,10 @@ snapshots: lru-cache@10.4.3: {} + lucide-svelte@0.462.0(svelte@5.2.7): + dependencies: + svelte: 5.2.7 + magic-string@0.30.13: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 @@ -2762,6 +2861,8 @@ snapshots: svelte: 5.2.7 vfile-message: 2.0.4 + media-typer@0.3.0: {} + merge2@1.4.1: {} micromatch@4.0.8: @@ -2769,6 +2870,12 @@ snapshots: braces: 3.0.3 picomatch: 2.3.1 + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + minimatch@3.1.2: dependencies: brace-expansion: 1.1.11 @@ -2777,14 +2884,30 @@ snapshots: dependencies: brace-expansion: 2.0.1 + minimist@1.2.8: {} + minipass@7.1.2: {} + mkdirp@0.5.6: + dependencies: + minimist: 1.2.8 + mri@1.2.0: {} mrmime@2.0.0: {} ms@2.1.3: {} + multer@1.4.5-lts.1: + dependencies: + append-field: 1.0.0 + busboy: 1.6.0 + concat-stream: 1.6.2 + mkdirp: 0.5.6 + object-assign: 4.1.1 + type-is: 1.6.18 + xtend: 4.0.2 + mz@2.7.0: dependencies: any-promise: 1.3.0 @@ -2934,6 +3057,8 @@ snapshots: prismjs@1.29.0: {} + process-nextick-args@2.0.1: {} + punycode@2.3.1: {} queue-microtask@1.2.3: {} @@ -2942,10 +3067,17 @@ snapshots: dependencies: pify: 2.3.0 + readable-stream@2.3.8: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 readable-stream@3.6.2: dependencies: inherits: 2.0.4 - string_decoder: 1.3.0 util-deprecate: 1.0.2 readdirp@3.6.0: @@ -3005,6 +3137,7 @@ snapshots: dependencies: mri: 1.2.0 + safe-buffer@5.1.2: {} safe-buffer@5.2.1: {} safe-stable-stringify@2.5.0: {} @@ -3033,6 +3166,7 @@ snapshots: source-map-js@1.2.1: {} + streamsearch@1.1.0: {} stack-trace@0.0.10: {} string-width@4.2.3: @@ -3047,6 +3181,9 @@ snapshots: emoji-regex: 9.2.2 strip-ansi: 7.1.0 + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 string_decoder@1.3.0: dependencies: safe-buffer: 5.2.1 @@ -3190,6 +3327,13 @@ snapshots: dependencies: prelude-ls: 1.2.1 + type-is@1.6.18: + dependencies: + media-typer: 0.3.0 + mime-types: 2.1.35 + + typedarray@0.0.6: {} + typescript-eslint@8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.7.2): dependencies: '@typescript-eslint/eslint-plugin': 8.15.0(@typescript-eslint/parser@8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.7.2))(eslint@9.15.0(jiti@1.21.6))(typescript@5.7.2) @@ -3277,6 +3421,7 @@ snapshots: string-width: 5.1.2 strip-ansi: 7.1.0 + xtend@4.0.2: {} yallist@4.0.0: {} yaml@1.10.2: {} diff --git a/src/lib/components/ui/Alert.svelte b/src/lib/components/ui/Alert.svelte new file mode 100644 index 0000000..8b40d1d --- /dev/null +++ b/src/lib/components/ui/Alert.svelte @@ -0,0 +1,66 @@ + + +{#if show} +
+ + {message} +
+{/if} + + diff --git a/src/lib/components/ui/ChatItem.svelte b/src/lib/components/ui/ChatItem.svelte new file mode 100644 index 0000000..5afb5ee --- /dev/null +++ b/src/lib/components/ui/ChatItem.svelte @@ -0,0 +1,19 @@ + + +
+
+

{title}

+

{lastMessage}

+
+

{time}

+
+ + \ No newline at end of file diff --git a/src/lib/components/ui/ChoosePicture.svelte b/src/lib/components/ui/ChoosePicture.svelte new file mode 100644 index 0000000..ede5fcd --- /dev/null +++ b/src/lib/components/ui/ChoosePicture.svelte @@ -0,0 +1,105 @@ + + + + + +
+ + Image de profil + + + + +
+ + +
+
diff --git a/src/lib/components/ui/CreateChat.svelte b/src/lib/components/ui/CreateChat.svelte new file mode 100644 index 0000000..226be15 --- /dev/null +++ b/src/lib/components/ui/CreateChat.svelte @@ -0,0 +1,73 @@ + + +{#if show} +
+
+

Créer un nouveau chat

+ + +
+
+{/if} + + + + diff --git a/src/lib/components/ui/ProfileCard.svelte b/src/lib/components/ui/ProfileCard.svelte new file mode 100644 index 0000000..89b1737 --- /dev/null +++ b/src/lib/components/ui/ProfileCard.svelte @@ -0,0 +1,71 @@ + + +{#if show} + +{/if} + + diff --git a/src/lib/components/ui/ProfileInfo.svelte b/src/lib/components/ui/ProfileInfo.svelte new file mode 100644 index 0000000..8b1d168 --- /dev/null +++ b/src/lib/components/ui/ProfileInfo.svelte @@ -0,0 +1,34 @@ + + +
+

{user.pseudo}

+

{user.prenom} {user.nom}

+

{user.description}

+
+ + diff --git a/src/lib/components/ui/Search.svelte b/src/lib/components/ui/Search.svelte new file mode 100644 index 0000000..749d684 --- /dev/null +++ b/src/lib/components/ui/Search.svelte @@ -0,0 +1,16 @@ + + +
+ + + +
+ diff --git a/src/routes/api/canal/[id]/+page.server.ts b/src/routes/api/canal/[id]/+page.server.ts new file mode 100644 index 0000000..cfdb6f3 --- /dev/null +++ b/src/routes/api/canal/[id]/+page.server.ts @@ -0,0 +1,123 @@ +import { json } from '@sveltejs/kit'; +import prisma from '$lib/prismaClient'; +import redisClient from '$lib/redisClient'; // Assurez-vous d'importer le client Redis + +// Récupérer les informations du canal et le dernier message (avec cache Redis) +export async function GET({ params }) { + const canalId = parseInt(params.id); + + // Clé cache pour les informations du canal et le dernier message + const canalCacheKey = `canal:${canalId}:info`; + + try { + // Vérifier si les informations du canal et le dernier message sont dans le cache Redis + const cachedCanalData = await redisClient.get(canalCacheKey); + if (cachedCanalData) { + console.log('✅ Cache hit pour les informations du canal et le dernier message'); + return json(JSON.parse(cachedCanalData)); + } + + console.log('❌ Cache miss'); + // Si non, récupérer les informations du canal et le dernier message depuis Prisma + const canal = await prisma.canal.findUnique({ + where: { id: canalId }, + include: { + users: true, // Inclut les utilisateurs associés au canal + }, + }); + + if (!canal) { + return json({ error: 'Canal non trouvé' }, { status: 404 }); + } + + // Récupérer le dernier message + const lastMessage = await prisma.message.findFirst({ + where: { canalId }, + include: { + user: { select: { id: true, pseudo: true } }, + }, + orderBy: { createdAt: 'desc' }, // Trie par date décroissante, donc le dernier message est récupéré en premier + }); + + // Créer un objet combiné pour le canal et le dernier message + const canalData = { + canal, + lastMessage, // Inclure uniquement le dernier message + }; + + // Mettre en cache les informations du canal et le dernier message pendant 5 minutes + await redisClient.set(canalCacheKey, JSON.stringify(canalData), 'EX', 300); // Cache pendant 5 minutes + + console.log('❌ Cache miss - Mise en cache des résultats'); + return json(canalData); + } catch (err) { + console.error(err); + return json({ error: 'Erreur lors de la récupération du canal ou du dernier message' }, { status: 500 }); + } +} + +// Supprimer un canal et invalider le cache associé +export async function DELETE({ params }) { + const canalId = parseInt(params.id); + + try { + // Supprimer le canal de la base de données + await prisma.canal.delete({ + where: { id: canalId }, + }); + + // Invalider le cache + await redisClient.del(`canal:${canalId}:info`); + + return json({ message: 'Canal supprimé avec succès' }); + } catch (err) { + console.error(err); + return json({ error: 'Erreur lors de la suppression du canal' }, { status: 500 }); + } +} + +// Modifier un canal +export async function PUT({ params, request }) { + const canalId = parseInt(params.id); + const { nom, domaine } = await request.json(); // On suppose que ce sont les champs à mettre à jour + + // Clé cache pour les informations du canal et le dernier message + const canalCacheKey = `canal:${canalId}:info`; + + try { + // Mettre à jour les informations du canal dans la base de données + const updatedCanal = await prisma.canal.update({ + where: { id: canalId }, + data: { + nom, // Nom du canal + domaine, // Domaine du canal + }, + include: { + users: true, // Inclut les utilisateurs associés au canal + }, + }); + + // Récupérer le dernier message associé au canal après mise à jour + const lastMessage = await prisma.message.findFirst({ + where: { canalId }, + include: { + user: { select: { id: true, pseudo: true } }, + }, + orderBy: { createdAt: 'desc' }, + }); + + // Créer un objet combiné pour les nouvelles informations du canal et le dernier message + const canalData = { + canal: updatedCanal, + lastMessage, // Inclure uniquement le dernier message + }; + + // Mettre en cache les nouvelles informations pendant 5 minutes + await redisClient.set(canalCacheKey, JSON.stringify(canalData), 'EX', 60 * 5); // Cache pendant 5 minutes + + return json(canalData); + } catch (err) { + console.error(err); + return json({ error: 'Erreur lors de la mise à jour du canal' }, { status: 500 }); + } +} diff --git a/src/routes/api/canal/[id]/messages/+page.server.ts b/src/routes/api/canal/[id]/messages/+page.server.ts new file mode 100644 index 0000000..9fd337b --- /dev/null +++ b/src/routes/api/canal/[id]/messages/+page.server.ts @@ -0,0 +1,149 @@ +import { json } from '@sveltejs/kit'; +import prisma from '$lib/prismaClient'; +import redisClient from '$lib/redisClient'; // Assure-toi d'importer ton client Redis + +export async function GET({ params, url }) { + const canalId = parseInt(params.id); + + // Gestion de la pagination avec des paramètres optionnels `page` et `limit` + const page = parseInt(url.searchParams.get('page')) || 1; + const limit = parseInt(url.searchParams.get('limit')) || 10; + const offset = (page - 1) * limit; + + // Générer une clé cache Redis unique en fonction du canal et des paramètres de pagination + const cacheKey = `canal:${canalId}:messages:page:${page}:limit:${limit}`; + + try { + // 1. Vérifier si les messages sont déjà dans le cache Redis + const cachedMessages = await redisClient.get(cacheKey); + if (cachedMessages) { + console.log('✅ Cache hit'); + return json(JSON.parse(cachedMessages)); // Si les données sont en cache, les retourner + } + + // 2. Si les messages ne sont pas en cache, récupérer depuis la base de données + const messages = await prisma.message.findMany({ + where: { canalId }, + include: { + user: { + select: { id: true, pseudo: true }, // Inclut uniquement l’ID et le pseudo de l’utilisateur + }, + }, + orderBy: { + createdAt: 'asc', // Trie par date croissante + }, + skip: offset, + take: limit, + }); + + // 3. Compter le nombre total de messages pour la pagination + const totalMessages = await prisma.message.count({ + where: { canalId }, + }); + + const response = { + messages, + pagination: { + page, + limit, + totalMessages, + totalPages: Math.ceil(totalMessages / limit), + }, + }; + + // 4. Mettre en cache les messages avec une expiration (par exemple 5 minutes) + await redisClient.set(cacheKey, JSON.stringify(response), 'EX', 60 * 5); // Cache pendant 5 minutes + + console.log('❌ Cache miss - Mise en cache des résultats'); + return json(response); // Retourner les données récupérées + } catch (err) { + console.error(err); + return json({ error: 'Erreur lors de la récupération des messages' }, { status: 500 }); + } +} + +export async function POST({ params, request }) { + const canalId = parseInt(params.id); + const { userId, text } = await request.json(); + + try { + // Créer un nouveau message dans la base de données + const newMessage = await prisma.message.create({ + data: { + userId, + canalId, + text, + }, + include: { user: { select: { id: true, pseudo: true } } }, + }); + + updateCaches(); // Mettre à jour les caches après la création d’un nouveau message + + return json(newMessage, { status: 201 }); + } catch (err) { + console.error(err); + return json({ error: 'Erreur lors de la création du message' }, { status: 500 }); + } +} + +export async function DELETE({ params }) { + const messageId = parseInt(params.id); + + try { + // Supprimer le message de la base de données + await prisma.message.delete({ + where: { id: messageId }, + }); + + updateCaches(); // Mettre à jour les caches après la suppression d’un message + + return json({ message: 'Message supprimé avec succès' }); + } catch (err) { + console.error(err); + return json({ error: 'Erreur lors de la suppression du message' }, { status: 500 }); + } +} + +// Fonction pour mettre à jour tous les caches des messages +function updateCaches(canalId) { + // Mettre à jour tous les caches + // Mettre à jour toutes les pages dans le cache + let page : number = 1; + let limit : number = 10; + let offset : number = (page - 1) * limit; + while (true) { + const cacheKey = `canal:${canalId}:messages:page:${page}:limit:${limit}`; + const cachedMessages = await redisClient.get(cacheKey); + if (!cachedMessages) { + break; + } + const totalMessages = await prisma.message.count({ + where: { canalId }, + }); + const messages = await prisma.message.findMany({ + where: { canalId }, + include: { + user: { + select: { id: true, pseudo: true }, + }, + }, + orderBy: { + createdAt: 'asc', + }, + skip: offset, + take: limit, + }); + const response = { + messages, + pagination: { + page, + limit, + totalMessages, + totalPages: Math.ceil(totalMessages / limit), + }, + }; + await redisClient.set(cacheKey, JSON.stringify(response), 'EX', 60 * 5); + page++; + offset = (page - 1) * limit; + } +} diff --git a/src/routes/api/canals/+page.server.ts b/src/routes/api/canals/+page.server.ts new file mode 100644 index 0000000..b154366 --- /dev/null +++ b/src/routes/api/canals/+page.server.ts @@ -0,0 +1,47 @@ +import { json } from '@sveltejs/kit'; +import prisma from '$lib/prismaClient'; +import redisClient from '$lib/redisClient'; + +// GET: Liste tous les canaux +export async function GET() { + try { + const cachedCanaux = await redisClient.get('canaux'); + if (cachedCanaux) { + console.log('✅ Cache hit'); + return json(JSON.parse(cachedCanaux)); + } + + console.log('❌ Cache miss'); + const canaux = await prisma.canal.findMany({ + include: { users: true, messages: true }, // Inclut les relations + }); + + await redisClient.set('canaux', JSON.stringify(canaux), { EX: 600 }); // Met en cache + return json(canaux); + } catch (err) { + console.error(err); + return json({ error: 'Erreur serveur' }, { status: 500 }); + } +} + +export async function POST({ request }) { + const { nom, domaine, userIds } = await request.json(); + + try { + const canal = await prisma.canal.create({ + data: { + nom, + domaine, + users: { + connect: userIds.map((id) => ({ id })), // Associe des utilisateurs au canal + }, + }, + }); + + return json(canal, { status: 201 }); + } catch (err) { + console.error(err); + return json({ error: 'Erreur lors de la création du canal' }, { status: 500 }); + } +} + diff --git a/src/routes/api/user/[id]/+page.server.ts b/src/routes/api/user/[id]/+page.server.ts new file mode 100644 index 0000000..3282d88 --- /dev/null +++ b/src/routes/api/user/[id]/+page.server.ts @@ -0,0 +1,200 @@ +import { json } from '@sveltejs/kit'; +import redisClient from '$lib/redisClient'; +import prisma from '$lib/prismaClient'; +import multer from 'multer'; +import path from 'path'; +import fs from 'fs'; + +const destinationDir = '/uploads'; + +const storage = multer.diskStorage({ + destination: (req, file, cb) => { + cb(null, `.${destinationDir}'); // Dossier où les images sont stockées` + }, + filename: (req, file, cb) => { + cb(null, `${Date.now()}-${file.originalname}`); + }, + fileFilter(req, file, cb) { + const fileExtension = path.extname(file.originalname).toLowerCase(); + if (fileExtension !== '.jpg' && fileExtension !== '.jpeg' && fileExtension !== '.png') { + return cb(new Error('Seules les images JPG, JPEG et PNG sont autorisées.')); + } + cb(null, true); + } +}); + +const upload = multer({ storage }); + +export async function GET({ params }) { + const userId = params.id; + + try { + // Vérifier si l'utilisateur est dans le cache Redis + const cachedUser = await redisClient.get(`user:${userId}`); + if (cachedUser) { + console.log('✅ Cache hit'); + return json(JSON.parse(cachedUser)); + } + + console.log('❌ Cache miss'); + // Si non, récupérer depuis MongoDB via Prisma + const user = await prisma.user.findUnique({ + where: { id: parseInt(userId) }, + }); + + if (!user) { + return json({ error: 'Utilisateur non trouvé' }, { status: 404 }); + } + + // Mettre l'utilisateur en cache + await redisClient.set(`user:${userId}`, JSON.stringify(user), { EX: 3600 }); + + return json(user); + } catch (err) { + console.error(err); + return json({ error: 'Erreur serveur' }, { status: 500 }); + } +} + +// Mettre à jour un utilisateur avec PUT +export async function PUT({ params, request }) { + const userId = parseInt(params.id); + + const cachedUser = await redisClient.get(`user:${userId}`); + // Récupérer l'utilisateur à partir de la base de données + let existingUser; + + if (cachedUser) { + console.log('✅ Cache hit'); + // Si l'utilisateur est dans le cache, on le parse + existingUser = JSON.parse(cachedUser); + } else { + // Si l'utilisateur n'est pas dans le cache, on le récupère de la base de données + console.log('❌ Cache miss'); + existingUser = await prisma.user.findUnique({ + where: { id: userId }, + }); + + if (!existingUser) { + return json({ error: 'Utilisateur non trouvé' }, { status: 404 }); + } + + // Utilisation de multer pour récupérer l'image (si présente) + return new Promise((resolve, reject) => { + upload.single('profilePicture')(request.raw, request.raw, async (err) => { + if (err) { + console.error('Erreur de téléchargement:', err); + return reject(json({ error: 'Erreur lors du téléchargement du fichier' }, { status: 500 })); + } + + // Extraire les autres données (pseudo, nom, etc.) du body de la requête + const { pseudo, nom, prenom, email, password } = await request.json(); + + let updatedUserData = { + pseudo, + nom, + prenom, + email, + password, // Assurez-vous de bien sécuriser les mots de passe + }; + + // Si une nouvelle image est envoyée + if (request.file) { + // Vérifiez si l'utilisateur a déjà une image + if (existingUser.profilePictureUrl) { + // Supprimer l'ancienne image + const oldImagePath = `.${destinationDir}/${path.basename(existingUser.profilePictureUrl)}`; + if (fs.existsSync(oldImagePath)) { + fs.unlinkSync(oldImagePath); // Suppression du fichier + } + } + + // Ajouter la nouvelle image à la base de données + updatedUserData.profilePictureUrl = `${destinationDir}/${request.file.filename}`; + } else if (!request.file && existingUser.profilePictureUrl) { + // Si aucune image n'est envoyée, supprimer l'image actuelle + const oldImagePath = `.${destinationDir}/${path.basename(existingUser.profilePictureUrl)}`; + if (fs.existsSync(oldImagePath)) { + fs.unlinkSync(oldImagePath); // Supprimer l'ancienne image + } + + // Mettre à jour l'URL de l'image en null + updatedUserData.profilePictureUrl = null; + } + + try { + // Mettre à jour l'utilisateur dans la base de données + const updatedUser = await prisma.user.update({ + where: { id: userId }, + data: updatedUserData, + }); + + // Mettre à jour l'utilisateur dans le cache Redis + await redisClient.set(`user:${userId}`, JSON.stringify(updatedUser), 'EX', 3600); // Cache pendant 1 heure (3600 secondes) + + // Réponse avec l'utilisateur mis à jour + return resolve(json(updatedUser)); + } catch (error) { + console.error('Erreur lors de la mise à jour de l\'utilisateur:', error); + return reject(json({ error: 'Erreur lors de la mise à jour de l\'utilisateur' }, { status: 500 })); + } + }); + }); +} + + +export async function DELETE({ params }) { + const userId = parseInt(params.id); + + try { + // Vérifier si l'utilisateur est dans le cache Redis + const cachedUser = await redisClient.get(`user:${userId}`); + let userToDelete; + + if (cachedUser) { + console.log('✅ Cache hit'); + // Si l'utilisateur est dans le cache, on le parse + userToDelete = JSON.parse(cachedUser); + } else { + // Si l'utilisateur n'est pas dans le cache, on le récupère de la base de données + console.log('❌ Cache miss'); + userToDelete = await prisma.user.findUnique({ + where: { id: userId }, + }); + + if (!userToDelete) { + return json({ error: 'Utilisateur non trouvé' }, { status: 404 }); + } + + // Mettre l'utilisateur dans le cache Redis + await redisClient.set(`user:${userId}`, JSON.stringify(userToDelete), { EX: 3600 }); // Cache pendant 1 heure + } + + // Si l'utilisateur a une image de profil, la supprimer + if (userToDelete.profilePictureUrl) { + // Calculer le chemin du fichier à supprimer + const imagePath = `.${destinationDir}/${path.basename(userToDelete.profilePictureUrl)}`; + if (fs.existsSync(imagePath)) { + fs.unlinkSync(imagePath); // Supprimer le fichier image + } + } + + // Supprimer l'utilisateur de la base de données + await prisma.user.delete({ + where: { id: userId }, + }); + + // Supprimer l'utilisateur du cache Redis + await redisClient.del(`user:${userId}`); + + // Réponse après suppression réussie + return json({ message: 'Utilisateur et image supprimés avec succès' }); + } catch (err) { + console.error(err); + return json({ error: 'Erreur lors de la suppression de l’utilisateur' }, { status: 500 }); + } +} + + + + diff --git a/src/routes/api/users/+page.server.ts b/src/routes/api/users/+page.server.ts new file mode 100644 index 0000000..7444e4e --- /dev/null +++ b/src/routes/api/users/+page.server.ts @@ -0,0 +1,87 @@ +// src/routes/api/users/+server.js +import { json } from '@sveltejs/kit'; +import redisClient from '$lib/redisClient'; +import prisma from '$lib/prismaClient'; +import multer from 'multer'; + +const destinationDir = '/uploads'; + +const storage = multer.diskStorage({ + destination: (req, file, cb) => { + cb(null, `.${destinationDir}'); // Dossier où les images sont stockées` + }, + filename: (req, file, cb) => { + cb(null, `${Date.now()}-${file.originalname}`); + }, + fileFilter(req, file, cb) { + const fileExtension = path.extname(file.originalname).toLowerCase(); + if (fileExtension !== '.jpg' && fileExtension !== '.jpeg' && fileExtension !== '.png') { + return cb(new Error('Seules les images JPG, JPEG et PNG sont autorisées.')); + } + cb(null, true); + } +}); + +export async function GET() { + try { + // Vérifier si les utilisateurs sont dans le cache Redis + const cachedUsers = await redisClient.get('users'); + if (cachedUsers) { + console.log('✅ Cache hit'); + return json(JSON.parse(cachedUsers)); + } + + console.log('❌ Cache miss'); + // Sinon, récupérer les utilisateurs depuis MongoDB + const users = await prisma.user.findMany(); + + // Mettre les utilisateurs en cache + await redisClient.set('users', JSON.stringify(users), { EX: 600 }); + + return json(users); + } catch (err) { + console.error(err); + return json({ error: 'Erreur serveur' }, { status: 500 }); + } +} + +export async function POST({ request }) { + return new Promise((resolve, reject) => { + // Utilisation de multer pour récupérer le fichier + upload.single('profilePicture')(request.raw, request.raw, async (err) => { + if (err) { + console.error('Erreur de téléchargement:', err); + return reject(json({ error: 'Erreur lors du téléchargement du fichier' }, { status: 500 })); + } + + // Récupérer les données du formulaire (sans le fichier) + const { pseudo, nom, prenom, email, password } = await request.json(); + + // L'URL de l'image sera le chemin relatif à partir du dossier uploads + const imageUrl = request.file ? `${destinationDir}/${request.file.filename}` : null; + + try { + // Créer un nouvel utilisateur avec l'URL de l'image + const user = await prisma.user.create({ + data: { + pseudo, + nom, + prenom, + email, + password, + profilePictureUrl: imageUrl, // Stocker l'URL de l'image + }, + }); + + // Mettre l'utilisateur dans le cache Redis + await redisClient.set(`user:${user.id}`, JSON.stringify(user), { EX: 3600 }); + + // Réponse avec les données de l'utilisateur + return resolve(json(user, { status: 201 })); + } catch (error) { + console.error('Erreur lors de la création de l\'utilisateur:', error); + return reject(json({ error: 'Erreur lors de la création de l\'utilisateur' }, { status: 500 })); + } + }); + }); +} diff --git a/src/routes/chat/+page.server.ts b/src/routes/chat/+page.server.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/routes/chat/+page.svelte b/src/routes/chat/+page.svelte deleted file mode 100644 index 6e7ab5b..0000000 --- a/src/routes/chat/+page.svelte +++ /dev/null @@ -1,3 +0,0 @@ - - -Salut \ No newline at end of file diff --git a/src/routes/chats/+page.svelte b/src/routes/chats/+page.svelte new file mode 100644 index 0000000..1cab2a3 --- /dev/null +++ b/src/routes/chats/+page.svelte @@ -0,0 +1,83 @@ + + +
+
+ + + +
+
+ +
+
+ + + +
+ +
+ + +
+ +
+ + + + diff --git a/src/routes/user/edit/+page.svelte b/src/routes/user/edit/+page.svelte new file mode 100644 index 0000000..ce54044 --- /dev/null +++ b/src/routes/user/edit/+page.svelte @@ -0,0 +1,140 @@ + + +
+
+

Modifier les informations du compte

+ + + {#if showMessage} +
+ {message} +
+ {/if} + + +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+ + {#if profilePicture} +
Image sélectionnée : {profilePicture.name}
+ {/if} +
+ +
+ +
+
+
+
+ + diff --git a/static/profile-default.svg b/static/profile-default.svg new file mode 100644 index 0000000..726ac6f --- /dev/null +++ b/static/profile-default.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file