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 @@
+
+
+
+
+
+
+
+
+
+
+
+ Sélectionner une image
+
+
+
+
+
+
+ Supprimer l'image
+
+
+
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
+
+
+ Créer
+
+
+
+{/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}
+
+
+
+
{user.prenom} {user.nom}
+
{user.description}
+
+
+{/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 @@
+
+
+
+
+
+
+
+ Profile
+
+
+
+
+
+
+
+ Nouveau Chat
+
+
+
+
+
+
+
+
+
+
+
+
+
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}
+
+
+
+
+
+
+
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