Merge branch 'dev' into features/front

This commit is contained in:
Bilal Dieumegard 2024-12-01 18:20:38 +01:00 committed by GitHub
commit ce7831b628
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 1229 additions and 37 deletions

2
.gitignore vendored
View file

@ -1,5 +1,7 @@
node_modules
/logs
# Output
.output
.vercel

41
docker-compose.yml Normal file
View file

@ -0,0 +1,41 @@
services:
mongodb:
build: ./mongodb_rs
hostname: mongodb
restart: always
environment:
- MONGO_INITDB_ROOT_USERNAME=temp-root-username
- MONGO_INITDB_ROOT_PASSWORD=temp-password
- MONGO_INITDB_DATABASE=chat_projetweb
- MONGO_REPLICA_HOST=localhost
- MONGO_REPLICA_PORT=27017
ports:
- "27017:27017"
volumes:
- mongo-data:/data/db/
- mongo-logs:/var/log/mongodb/
networks:
- mongodb_network
redis:
image: redis:latest
hostname: redis
restart: always
ports:
- "6379:6379"
command: redis-server --save 20 1 --loglevel warning
environment:
- REDIS_PASSWORD=temp-redis-password
volumes:
- redis-data:/root/redis
networks:
- redis_network
networks:
mongodb_network:
redis_network:
volumes:
mongo-data:
mongo-logs:
redis-data:

11
mongodb_rs/Dockerfile Normal file
View file

@ -0,0 +1,11 @@
FROM mongo:5
# we take over the default & start mongo in replica set mode in a background task
ENTRYPOINT mongod --port $MONGO_REPLICA_PORT --replSet rs0 --bind_ip 0.0.0.0 & MONGOD_PID=$!; \
# we prepare the replica set with a single node and prepare the root user config
INIT_REPL_CMD="rs.initiate({ _id: 'rs0', members: [{ _id: 0, host: '$MONGO_REPLICA_HOST:$MONGO_REPLICA_PORT' }] })"; \
INIT_USER_CMD="db.getUser('$MONGO_INITDB_ROOT_USERNAME') || db.createUser({ user: '$MONGO_INITDB_ROOT_USERNAME', pwd: '$MONGO_INITDB_ROOT_PASSWORD', roles: ['root'] })"; \
# we wait for the replica set to be ready and then submit the command just above
until (mongo admin --port $MONGO_REPLICA_PORT --eval "$INIT_REPL_CMD && $INIT_USER_CMD"); do sleep 1; done; \
# we are done but we keep the container by waiting on signals from the mongo task
echo "REPLICA SET ONLINE"; wait $MONGOD_PID;

View file

@ -39,5 +39,12 @@
"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",
"prisma": "^5.22.0",
"redis": "^4.7.0",
"svelte-radix": "^2.0.1",
"winston": "^3.17.0"
}
}

420
pnpm-lock.yaml generated
View file

@ -14,19 +14,37 @@ importers:
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)
'@types/node':
specifier: ^22.10.1
version: 22.10.1
argon2:
specifier: ^0.41.1
version: 0.41.1
prisma:
specifier: ^5.22.0
version: 5.22.0
redis:
specifier: ^4.7.0
version: 4.7.0
svelte-radix:
specifier: ^2.0.1
version: 2.0.1(svelte@5.2.7)
winston:
specifier: ^3.17.0
version: 3.17.0
devDependencies:
'@sveltejs/adapter-auto':
specifier: ^3.0.0
version: 3.3.1(@sveltejs/kit@2.8.3(@sveltejs/vite-plugin-svelte@4.0.2(svelte@5.2.7)(vite@5.4.11))(svelte@5.2.7)(vite@5.4.11))
version: 3.3.1(@sveltejs/kit@2.8.3(@sveltejs/vite-plugin-svelte@4.0.2(svelte@5.2.7)(vite@5.4.11(@types/node@22.10.1)))(svelte@5.2.7)(vite@5.4.11(@types/node@22.10.1)))
'@sveltejs/kit':
specifier: ^2.0.0
version: 2.8.3(@sveltejs/vite-plugin-svelte@4.0.2(svelte@5.2.7)(vite@5.4.11))(svelte@5.2.7)(vite@5.4.11)
version: 2.8.3(@sveltejs/vite-plugin-svelte@4.0.2(svelte@5.2.7)(vite@5.4.11(@types/node@22.10.1)))(svelte@5.2.7)(vite@5.4.11(@types/node@22.10.1))
'@sveltejs/vite-plugin-svelte':
specifier: ^4.0.0
version: 4.0.2(svelte@5.2.7)(vite@5.4.11)
version: 4.0.2(svelte@5.2.7)(vite@5.4.11(@types/node@22.10.1))
autoprefixer:
specifier: ^10.4.20
version: 10.4.20(postcss@8.4.49)
@ -83,7 +101,7 @@ importers:
version: 8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.7.2)
vite:
specifier: ^5.0.3
version: 5.4.11
version: 5.4.11(@types/node@22.10.1)
packages:
@ -95,6 +113,13 @@ packages:
resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
engines: {node: '>=6.0.0'}
'@colors/colors@1.6.0':
resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==}
engines: {node: '>=0.1.90'}
'@dabh/diagnostics@2.0.3':
resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==}
'@esbuild/aix-ppc64@0.21.5':
resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==}
engines: {node: '>=12'}
@ -338,6 +363,10 @@ packages:
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
engines: {node: '>= 8'}
'@phc/format@1.0.0':
resolution: {integrity: sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ==}
engines: {node: '>=10'}
'@pkgjs/parseargs@0.11.0':
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
@ -345,6 +374,59 @@ packages:
'@polka/url@1.0.0-next.28':
resolution: {integrity: sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==}
'@prisma/client@5.22.0':
resolution: {integrity: sha512-M0SVXfyHnQREBKxCgyo7sffrKttwE6R8PMq330MIUF0pTwjUhLbW84pFDlf06B27XyCR++VtjugEnIHdr07SVA==}
engines: {node: '>=16.13'}
peerDependencies:
prisma: '*'
peerDependenciesMeta:
prisma:
optional: true
'@prisma/debug@5.22.0':
resolution: {integrity: sha512-AUt44v3YJeggO2ZU5BkXI7M4hu9BF2zzH2iF2V5pyXT/lRTyWiElZ7It+bRH1EshoMRxHgpYg4VB6rCM+mG5jQ==}
'@prisma/engines-version@5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2':
resolution: {integrity: sha512-2PTmxFR2yHW/eB3uqWtcgRcgAbG1rwG9ZriSvQw+nnb7c4uCr3RAcGMb6/zfE88SKlC1Nj2ziUvc96Z379mHgQ==}
'@prisma/engines@5.22.0':
resolution: {integrity: sha512-UNjfslWhAt06kVL3CjkuYpHAWSO6L4kDCVPegV6itt7nD1kSJavd3vhgAEhjglLJJKEdJ7oIqDJ+yHk6qO8gPA==}
'@prisma/fetch-engine@5.22.0':
resolution: {integrity: sha512-bkrD/Mc2fSvkQBV5EpoFcZ87AvOgDxbG99488a5cexp5Ccny+UM6MAe/UFkUC0wLYD9+9befNOqGiIJhhq+HbA==}
'@prisma/get-platform@5.22.0':
resolution: {integrity: sha512-pHhpQdr1UPFpt+zFfnPazhulaZYCUqeIcPpJViYoq9R+D/yw4fjE+CtnsnKzPYm0ddUbeXUzjGVGIRVgPDCk4Q==}
'@redis/bloom@1.2.0':
resolution: {integrity: sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==}
peerDependencies:
'@redis/client': ^1.0.0
'@redis/client@1.6.0':
resolution: {integrity: sha512-aR0uffYI700OEEH4gYnitAnv3vzVGXCFvYfdpu/CJKvk4pHfLPEy/JSZyrpQ+15WhXe1yJRXLtfQ84s4mEXnPg==}
engines: {node: '>=14'}
'@redis/graph@1.1.1':
resolution: {integrity: sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==}
peerDependencies:
'@redis/client': ^1.0.0
'@redis/json@1.0.7':
resolution: {integrity: sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ==}
peerDependencies:
'@redis/client': ^1.0.0
'@redis/search@1.2.0':
resolution: {integrity: sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw==}
peerDependencies:
'@redis/client': ^1.0.0
'@redis/time-series@1.1.0':
resolution: {integrity: sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g==}
peerDependencies:
'@redis/client': ^1.0.0
'@rollup/rollup-android-arm-eabi@4.27.4':
resolution: {integrity: sha512-2Y3JT6f5MrQkICUyRVCw4oa0sutfAsgaSsb0Lmmy1Wi2y7X5vT9Euqw4gOsCyy0YfKURBg35nhUKZS4mDcfULw==}
cpu: [arm]
@ -476,6 +558,12 @@ packages:
'@types/json-schema@7.0.15':
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
'@types/node@22.10.1':
resolution: {integrity: sha512-qKgsUwfHZV2WCWLAnVP1JqnpE6Im6h3Y0+fYgMTasNQ7V++CBX5OT1as0g0f+OyubbFqhf6XVNIsmN4IIhEgGQ==}
'@types/triple-beam@1.3.5':
resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==}
'@types/unist@2.0.11':
resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==}
@ -588,6 +676,10 @@ packages:
arg@5.0.2:
resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
argon2@0.41.1:
resolution: {integrity: sha512-dqCW8kJXke8Ik+McUcMDltrbuAWETPyU6iq+4AhxqKphWi7pChB/Zgd/Tp/o8xRLbg8ksMj46F/vph9wnxpTzQ==}
engines: {node: '>=16.17.0'}
argparse@2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
@ -595,6 +687,9 @@ packages:
resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==}
engines: {node: '>= 0.4'}
async@3.2.6:
resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==}
autoprefixer@10.4.20:
resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==}
engines: {node: ^10 || ^12 || >=14}
@ -667,13 +762,32 @@ packages:
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
engines: {node: '>=6'}
cluster-key-slot@1.1.2:
resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==}
engines: {node: '>=0.10.0'}
color-convert@1.9.3:
resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
color-convert@2.0.1:
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
engines: {node: '>=7.0.0'}
color-name@1.1.3:
resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
color-name@1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
color-string@1.9.1:
resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==}
color@3.2.1:
resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==}
colorspace@1.1.4:
resolution: {integrity: sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==}
commander@4.1.1:
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
engines: {node: '>= 6'}
@ -742,6 +856,9 @@ packages:
emoji-regex@9.2.2:
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
enabled@2.0.0:
resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==}
esbuild@0.21.5:
resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==}
engines: {node: '>=12'}
@ -857,6 +974,9 @@ packages:
picomatch:
optional: true
fecha@4.2.3:
resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==}
file-entry-cache@8.0.0:
resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
engines: {node: '>=16.0.0'}
@ -876,6 +996,9 @@ packages:
flatted@3.3.2:
resolution: {integrity: sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==}
fn.name@1.1.0:
resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==}
focus-trap@7.6.2:
resolution: {integrity: sha512-9FhUxK1hVju2+AiQIDJ5Dd//9R2n2RAfJ0qfhF4IHGHgcoEUTMpbTeG/zbEuwaiYXfuAH6XE0/aCyxDdRM+W5w==}
@ -894,6 +1017,10 @@ packages:
function-bind@1.1.2:
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
generic-pool@3.9.0:
resolution: {integrity: sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==}
engines: {node: '>= 4'}
glob-parent@5.1.2:
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
engines: {node: '>= 6'}
@ -949,6 +1076,10 @@ packages:
inherits@2.0.4:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
is-arrayish@0.3.2:
resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==}
is-binary-path@2.1.0:
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
engines: {node: '>=8'}
@ -976,8 +1107,12 @@ 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'}
isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
@ -1012,6 +1147,9 @@ packages:
known-css-properties@0.35.0:
resolution: {integrity: sha512-a/RAk2BfKk+WFGhhOCAYqSiFLc34k8Mt/6NWRI4joER0EYUzXIcFivjjnoD3+XU1DggLn/tZc3DOAgke7l8a4A==}
kuler@2.0.0:
resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==}
levn@0.4.1:
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
engines: {node: '>= 0.8.0'}
@ -1037,6 +1175,10 @@ packages:
lodash.merge@4.6.2:
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
logform@2.7.0:
resolution: {integrity: sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==}
engines: {node: '>= 12.0.0'}
lru-cache@10.4.3:
resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
@ -1122,6 +1264,14 @@ packages:
natural-compare@1.4.0:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
node-addon-api@8.3.0:
resolution: {integrity: sha512-8VOpLHFrOQlAH+qA0ZzuGRlALRA6/LVh8QJldbrC4DY0hXoMP0l4Acq8TzFC018HztWiRqyCEj2aTWY2UvnJUg==}
engines: {node: ^18 || ^20 || >= 21}
node-gyp-build@4.8.4:
resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==}
hasBin: true
node-releases@2.0.18:
resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==}
@ -1141,6 +1291,9 @@ packages:
resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
engines: {node: '>= 6'}
one-time@1.0.0:
resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==}
optionator@0.9.4:
resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
engines: {node: '>= 0.8.0'}
@ -1328,6 +1481,11 @@ packages:
prism-svelte@0.4.7:
resolution: {integrity: sha512-yABh19CYbM24V7aS7TuPYRNMqthxwbvx6FF/Rw920YbyBWO3tnyPIqRMgHuSVsLmuHkkBS1Akyof463FVdkeDQ==}
prisma@5.22.0:
resolution: {integrity: sha512-vtpjW3XuYCSnMsNVBjLMNkTj6OZbudcPPTPYHqX0CJfpcdWciI1dM8uHETwmDxxiqEwCIE6WvXucWUetJgfu/A==}
engines: {node: '>=16.13'}
hasBin: true
prismjs@1.29.0:
resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==}
engines: {node: '>=6'}
@ -1347,6 +1505,9 @@ packages:
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'}
readdirp@3.6.0:
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
@ -1356,6 +1517,9 @@ packages:
resolution: {integrity: sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==}
engines: {node: '>= 14.16.0'}
redis@4.7.0:
resolution: {integrity: sha512-zvmkHEAdGMn+hMRXuMBtu4Vo5P6rHQjLoHftu+lBqq8ZTA3RCVC/WzD790bkKKiNFp7d5/9PcSD19fJyyRvOdQ==}
resolve-from@4.0.0:
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
engines: {node: '>=4'}
@ -1382,6 +1546,12 @@ packages:
safe-buffer@5.1.2:
resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
safe-buffer@5.2.1:
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
safe-stable-stringify@2.5.0:
resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==}
engines: {node: '>=10'}
semver@7.6.3:
resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==}
@ -1403,6 +1573,9 @@ packages:
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
engines: {node: '>=14'}
simple-swizzle@0.2.2:
resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==}
sirv@3.0.0:
resolution: {integrity: sha512-BPwJGUeDaDCHihkORDchNyyTvWFhcusy1XMmhEVTQTwGeybFbp8YEmB+njbPnth1FibULBSBVwCQni25XlCUDg==}
engines: {node: '>=18'}
@ -1414,6 +1587,8 @@ packages:
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==}
string-width@4.2.3:
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
@ -1425,6 +1600,8 @@ packages:
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==}
strip-ansi@6.0.1:
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
@ -1495,6 +1672,9 @@ packages:
engines: {node: '>=14.0.0'}
hasBin: true
text-hex@1.0.0:
resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==}
thenify-all@1.6.0:
resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==}
engines: {node: '>=0.8'}
@ -1513,6 +1693,10 @@ packages:
resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==}
engines: {node: '>=6'}
triple-beam@1.4.1:
resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==}
engines: {node: '>= 14.0.0'}
ts-api-utils@1.4.1:
resolution: {integrity: sha512-5RU2/lxTA3YUZxju61HO2U6EoZLvBLtmV2mbTvqyu4a/7s7RmJPT+1YekhMVsQhznRWk/czIwDUg+V8Q9ZuG4w==}
engines: {node: '>=16'}
@ -1551,6 +1735,9 @@ packages:
engines: {node: '>=14.17'}
hasBin: true
undici-types@6.20.0:
resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==}
unist-util-stringify-position@2.0.3:
resolution: {integrity: sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==}
@ -1613,6 +1800,14 @@ packages:
engines: {node: '>= 8'}
hasBin: true
winston-transport@4.9.0:
resolution: {integrity: sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==}
engines: {node: '>= 12.0.0'}
winston@3.17.0:
resolution: {integrity: sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==}
engines: {node: '>= 12.0.0'}
word-wrap@1.2.5:
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
engines: {node: '>=0.10.0'}
@ -1628,6 +1823,8 @@ packages:
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==}
yaml@1.10.2:
resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==}
@ -1654,6 +1851,14 @@ snapshots:
'@jridgewell/gen-mapping': 0.3.5
'@jridgewell/trace-mapping': 0.3.25
'@colors/colors@1.6.0': {}
'@dabh/diagnostics@2.0.3':
dependencies:
colorspace: 1.1.4
enabled: 2.0.0
kuler: 2.0.0
'@esbuild/aix-ppc64@0.21.5':
optional: true
@ -1838,11 +2043,64 @@ snapshots:
'@nodelib/fs.scandir': 2.1.5
fastq: 1.17.1
'@phc/format@1.0.0': {}
'@pkgjs/parseargs@0.11.0':
optional: true
'@polka/url@1.0.0-next.28': {}
'@prisma/client@5.22.0(prisma@5.22.0)':
optionalDependencies:
prisma: 5.22.0
'@prisma/debug@5.22.0': {}
'@prisma/engines-version@5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2': {}
'@prisma/engines@5.22.0':
dependencies:
'@prisma/debug': 5.22.0
'@prisma/engines-version': 5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2
'@prisma/fetch-engine': 5.22.0
'@prisma/get-platform': 5.22.0
'@prisma/fetch-engine@5.22.0':
dependencies:
'@prisma/debug': 5.22.0
'@prisma/engines-version': 5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2
'@prisma/get-platform': 5.22.0
'@prisma/get-platform@5.22.0':
dependencies:
'@prisma/debug': 5.22.0
'@redis/bloom@1.2.0(@redis/client@1.6.0)':
dependencies:
'@redis/client': 1.6.0
'@redis/client@1.6.0':
dependencies:
cluster-key-slot: 1.1.2
generic-pool: 3.9.0
yallist: 4.0.0
'@redis/graph@1.1.1(@redis/client@1.6.0)':
dependencies:
'@redis/client': 1.6.0
'@redis/json@1.0.7(@redis/client@1.6.0)':
dependencies:
'@redis/client': 1.6.0
'@redis/search@1.2.0(@redis/client@1.6.0)':
dependencies:
'@redis/client': 1.6.0
'@redis/time-series@1.1.0(@redis/client@1.6.0)':
dependencies:
'@redis/client': 1.6.0
'@rollup/rollup-android-arm-eabi@4.27.4':
optional: true
@ -1897,14 +2155,14 @@ snapshots:
'@rollup/rollup-win32-x64-msvc@4.27.4':
optional: true
'@sveltejs/adapter-auto@3.3.1(@sveltejs/kit@2.8.3(@sveltejs/vite-plugin-svelte@4.0.2(svelte@5.2.7)(vite@5.4.11))(svelte@5.2.7)(vite@5.4.11))':
'@sveltejs/adapter-auto@3.3.1(@sveltejs/kit@2.8.3(@sveltejs/vite-plugin-svelte@4.0.2(svelte@5.2.7)(vite@5.4.11(@types/node@22.10.1)))(svelte@5.2.7)(vite@5.4.11(@types/node@22.10.1)))':
dependencies:
'@sveltejs/kit': 2.8.3(@sveltejs/vite-plugin-svelte@4.0.2(svelte@5.2.7)(vite@5.4.11))(svelte@5.2.7)(vite@5.4.11)
'@sveltejs/kit': 2.8.3(@sveltejs/vite-plugin-svelte@4.0.2(svelte@5.2.7)(vite@5.4.11(@types/node@22.10.1)))(svelte@5.2.7)(vite@5.4.11(@types/node@22.10.1))
import-meta-resolve: 4.1.0
'@sveltejs/kit@2.8.3(@sveltejs/vite-plugin-svelte@4.0.2(svelte@5.2.7)(vite@5.4.11))(svelte@5.2.7)(vite@5.4.11)':
'@sveltejs/kit@2.8.3(@sveltejs/vite-plugin-svelte@4.0.2(svelte@5.2.7)(vite@5.4.11(@types/node@22.10.1)))(svelte@5.2.7)(vite@5.4.11(@types/node@22.10.1))':
dependencies:
'@sveltejs/vite-plugin-svelte': 4.0.2(svelte@5.2.7)(vite@5.4.11)
'@sveltejs/vite-plugin-svelte': 4.0.2(svelte@5.2.7)(vite@5.4.11(@types/node@22.10.1))
'@types/cookie': 0.6.0
cookie: 0.6.0
devalue: 5.1.1
@ -1918,27 +2176,27 @@ snapshots:
sirv: 3.0.0
svelte: 5.2.7
tiny-glob: 0.2.9
vite: 5.4.11
vite: 5.4.11(@types/node@22.10.1)
'@sveltejs/vite-plugin-svelte-inspector@3.0.1(@sveltejs/vite-plugin-svelte@4.0.2(svelte@5.2.7)(vite@5.4.11))(svelte@5.2.7)(vite@5.4.11)':
'@sveltejs/vite-plugin-svelte-inspector@3.0.1(@sveltejs/vite-plugin-svelte@4.0.2(svelte@5.2.7)(vite@5.4.11(@types/node@22.10.1)))(svelte@5.2.7)(vite@5.4.11(@types/node@22.10.1))':
dependencies:
'@sveltejs/vite-plugin-svelte': 4.0.2(svelte@5.2.7)(vite@5.4.11)
'@sveltejs/vite-plugin-svelte': 4.0.2(svelte@5.2.7)(vite@5.4.11(@types/node@22.10.1))
debug: 4.3.7
svelte: 5.2.7
vite: 5.4.11
vite: 5.4.11(@types/node@22.10.1)
transitivePeerDependencies:
- supports-color
'@sveltejs/vite-plugin-svelte@4.0.2(svelte@5.2.7)(vite@5.4.11)':
'@sveltejs/vite-plugin-svelte@4.0.2(svelte@5.2.7)(vite@5.4.11(@types/node@22.10.1))':
dependencies:
'@sveltejs/vite-plugin-svelte-inspector': 3.0.1(@sveltejs/vite-plugin-svelte@4.0.2(svelte@5.2.7)(vite@5.4.11))(svelte@5.2.7)(vite@5.4.11)
'@sveltejs/vite-plugin-svelte-inspector': 3.0.1(@sveltejs/vite-plugin-svelte@4.0.2(svelte@5.2.7)(vite@5.4.11(@types/node@22.10.1)))(svelte@5.2.7)(vite@5.4.11(@types/node@22.10.1))
debug: 4.3.7
deepmerge: 4.3.1
kleur: 4.1.5
magic-string: 0.30.13
svelte: 5.2.7
vite: 5.4.11
vitefu: 1.0.3(vite@5.4.11)
vite: 5.4.11(@types/node@22.10.1)
vitefu: 1.0.3(vite@5.4.11(@types/node@22.10.1))
transitivePeerDependencies:
- supports-color
@ -1952,6 +2210,12 @@ snapshots:
'@types/json-schema@7.0.15': {}
'@types/node@22.10.1':
dependencies:
undici-types: 6.20.0
'@types/triple-beam@1.3.5': {}
'@types/unist@2.0.11': {}
'@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)':
@ -2074,10 +2338,18 @@ snapshots:
arg@5.0.2: {}
argon2@0.41.1:
dependencies:
'@phc/format': 1.0.0
node-addon-api: 8.3.0
node-gyp-build: 4.8.4
argparse@2.0.1: {}
aria-query@5.3.2: {}
async@3.2.6: {}
autoprefixer@10.4.20(postcss@8.4.49):
dependencies:
browserslist: 4.24.2
@ -2156,12 +2428,35 @@ snapshots:
clsx@2.1.1: {}
cluster-key-slot@1.1.2: {}
color-convert@1.9.3:
dependencies:
color-name: 1.1.3
color-convert@2.0.1:
dependencies:
color-name: 1.1.4
color-name@1.1.3: {}
color-name@1.1.4: {}
color-string@1.9.1:
dependencies:
color-name: 1.1.4
simple-swizzle: 0.2.2
color@3.2.1:
dependencies:
color-convert: 1.9.3
color-string: 1.9.1
colorspace@1.1.4:
dependencies:
color: 3.2.1
text-hex: 1.0.0
commander@4.1.1: {}
concat-map@0.0.1: {}
@ -2209,6 +2504,8 @@ snapshots:
emoji-regex@9.2.2: {}
enabled@2.0.0: {}
esbuild@0.21.5:
optionalDependencies:
'@esbuild/aix-ppc64': 0.21.5
@ -2373,6 +2670,8 @@ snapshots:
fdir@6.4.2: {}
fecha@4.2.3: {}
file-entry-cache@8.0.0:
dependencies:
flat-cache: 4.0.1
@ -2393,6 +2692,8 @@ snapshots:
flatted@3.3.2: {}
fn.name@1.1.0: {}
focus-trap@7.6.2:
dependencies:
tabbable: 6.2.0
@ -2409,6 +2710,8 @@ snapshots:
function-bind@1.1.2: {}
generic-pool@3.9.0: {}
glob-parent@5.1.2:
dependencies:
is-glob: 4.0.3
@ -2455,6 +2758,8 @@ snapshots:
inherits@2.0.4: {}
is-arrayish@0.3.2: {}
is-binary-path@2.1.0:
dependencies:
binary-extensions: 2.3.0
@ -2478,6 +2783,7 @@ snapshots:
'@types/estree': 1.0.6
isarray@1.0.0: {}
is-stream@2.0.1: {}
isexe@2.0.0: {}
@ -2507,6 +2813,8 @@ snapshots:
known-css-properties@0.35.0: {}
kuler@2.0.0: {}
levn@0.4.1:
dependencies:
prelude-ls: 1.2.1
@ -2526,6 +2834,15 @@ snapshots:
lodash.merge@4.6.2: {}
logform@2.7.0:
dependencies:
'@colors/colors': 1.6.0
'@types/triple-beam': 1.3.5
fecha: 4.2.3
ms: 2.1.3
safe-stable-stringify: 2.5.0
triple-beam: 1.4.1
lru-cache@10.4.3: {}
lucide-svelte@0.462.0(svelte@5.2.7):
@ -2603,6 +2920,10 @@ snapshots:
natural-compare@1.4.0: {}
node-addon-api@8.3.0: {}
node-gyp-build@4.8.4: {}
node-releases@2.0.18: {}
normalize-path@3.0.0: {}
@ -2613,6 +2934,10 @@ snapshots:
object-hash@3.0.0: {}
one-time@1.0.0:
dependencies:
fn.name: 1.1.0
optionator@0.9.4:
dependencies:
deep-is: 0.1.4
@ -2724,6 +3049,12 @@ snapshots:
prism-svelte@0.4.7: {}
prisma@5.22.0:
dependencies:
'@prisma/engines': 5.22.0
optionalDependencies:
fsevents: 2.3.3
prismjs@1.29.0: {}
process-nextick-args@2.0.1: {}
@ -2744,6 +3075,9 @@ snapshots:
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
util-deprecate: 1.0.2
readdirp@3.6.0:
@ -2752,6 +3086,15 @@ snapshots:
readdirp@4.0.2: {}
redis@4.7.0:
dependencies:
'@redis/bloom': 1.2.0(@redis/client@1.6.0)
'@redis/client': 1.6.0
'@redis/graph': 1.1.1(@redis/client@1.6.0)
'@redis/json': 1.0.7(@redis/client@1.6.0)
'@redis/search': 1.2.0(@redis/client@1.6.0)
'@redis/time-series': 1.1.0(@redis/client@1.6.0)
resolve-from@4.0.0: {}
resolve@1.22.8:
@ -2795,6 +3138,9 @@ snapshots:
mri: 1.2.0
safe-buffer@5.1.2: {}
safe-buffer@5.2.1: {}
safe-stable-stringify@2.5.0: {}
semver@7.6.3: {}
@ -2808,6 +3154,10 @@ snapshots:
signal-exit@4.1.0: {}
simple-swizzle@0.2.2:
dependencies:
is-arrayish: 0.3.2
sirv@3.0.0:
dependencies:
'@polka/url': 1.0.0-next.28
@ -2817,6 +3167,7 @@ snapshots:
source-map-js@1.2.1: {}
streamsearch@1.1.0: {}
stack-trace@0.0.10: {}
string-width@4.2.3:
dependencies:
@ -2833,6 +3184,9 @@ snapshots:
string_decoder@1.1.1:
dependencies:
safe-buffer: 5.1.2
string_decoder@1.3.0:
dependencies:
safe-buffer: 5.2.1
strip-ansi@6.0.1:
dependencies:
@ -2938,6 +3292,8 @@ snapshots:
transitivePeerDependencies:
- ts-node
text-hex@1.0.0: {}
thenify-all@1.6.0:
dependencies:
thenify: 3.3.1
@ -2957,6 +3313,8 @@ snapshots:
totalist@3.0.1: {}
triple-beam@1.4.1: {}
ts-api-utils@1.4.1(typescript@5.7.2):
dependencies:
typescript: 5.7.2
@ -2989,6 +3347,8 @@ snapshots:
typescript@5.7.2: {}
undici-types@6.20.0: {}
unist-util-stringify-position@2.0.3:
dependencies:
'@types/unist': 2.0.11
@ -3010,22 +3370,43 @@ snapshots:
'@types/unist': 2.0.11
unist-util-stringify-position: 2.0.3
vite@5.4.11:
vite@5.4.11(@types/node@22.10.1):
dependencies:
esbuild: 0.21.5
postcss: 8.4.49
rollup: 4.27.4
optionalDependencies:
'@types/node': 22.10.1
fsevents: 2.3.3
vitefu@1.0.3(vite@5.4.11):
vitefu@1.0.3(vite@5.4.11(@types/node@22.10.1)):
optionalDependencies:
vite: 5.4.11
vite: 5.4.11(@types/node@22.10.1)
which@2.0.2:
dependencies:
isexe: 2.0.0
winston-transport@4.9.0:
dependencies:
logform: 2.7.0
readable-stream: 3.6.2
triple-beam: 1.4.1
winston@3.17.0:
dependencies:
'@colors/colors': 1.6.0
'@dabh/diagnostics': 2.0.3
async: 3.2.6
is-stream: 2.0.1
logform: 2.7.0
one-time: 1.0.0
readable-stream: 3.6.2
safe-stable-stringify: 2.5.0
stack-trace: 0.0.10
triple-beam: 1.4.1
winston-transport: 4.9.0
word-wrap@1.2.5: {}
wrap-ansi@7.0.0:
@ -3041,6 +3422,7 @@ snapshots:
strip-ansi: 7.1.0
xtend@4.0.2: {}
yallist@4.0.0: {}
yaml@1.10.2: {}

51
prisma/schema.prisma Normal file
View file

@ -0,0 +1,51 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mongodb"
url = env("DATABASE_URL")
}
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
username String @unique
surname String
name String
email String @unique
password String
channels Channel[] @relation(fields: [channelIDs], references: [id])
channelIDs String[] @db.ObjectId
messages Message[]
@@map("users") // Table name in DB
}
model Channel {
id String @id @default(auto()) @map("_id") @db.ObjectId
name String
topic String
users User[] @relation(fields: [userIDs], references: [id])
userIDs String[] @db.ObjectId
messages Message[]
@@map("channels") // Table name in DB
}
model Message {
id String @id @default(auto()) @map("_id") @db.ObjectId
user User @relation(fields: [userId], references: [id])
userId String @db.ObjectId
channel Channel @relation(fields: [channelId], references: [id])
channelId String @db.ObjectId
text String
createdAt DateTime @default(now())
@@map("messages") // Table name in DB
}

View file

@ -0,0 +1,18 @@
import { Tabs as TabsPrimitive } from "bits-ui";
import Content from "./tabs-content.svelte";
import List from "./tabs-list.svelte";
import Trigger from "./tabs-trigger.svelte";
const Root = TabsPrimitive.Root;
export {
Root,
Content,
List,
Trigger,
//
Root as Tabs,
Content as TabsContent,
List as TabsList,
Trigger as TabsTrigger,
};

View file

@ -0,0 +1,21 @@
<script lang="ts">
import { Tabs as TabsPrimitive } from "bits-ui";
import { cn } from "$lib/utils.js";
type $$Props = TabsPrimitive.ContentProps;
let className: $$Props["class"] = undefined;
export let value: $$Props["value"];
export { className as class };
</script>
<TabsPrimitive.Content
class={cn(
"ring-offset-background focus-visible:ring-ring mt-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2",
className
)}
{value}
{...$$restProps}
>
<slot />
</TabsPrimitive.Content>

View file

@ -0,0 +1,19 @@
<script lang="ts">
import { Tabs as TabsPrimitive } from "bits-ui";
import { cn } from "$lib/utils.js";
type $$Props = TabsPrimitive.ListProps;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<TabsPrimitive.List
class={cn(
"bg-muted text-muted-foreground inline-flex h-10 items-center justify-center rounded-md p-1",
className
)}
{...$$restProps}
>
<slot />
</TabsPrimitive.List>

View file

@ -0,0 +1,23 @@
<script lang="ts">
import { Tabs as TabsPrimitive } from "bits-ui";
import { cn } from "$lib/utils.js";
type $$Props = TabsPrimitive.TriggerProps;
type $$Events = TabsPrimitive.TriggerEvents;
let className: $$Props["class"] = undefined;
export let value: $$Props["value"];
export { className as class };
</script>
<TabsPrimitive.Trigger
class={cn(
"ring-offset-background focus-visible:ring-ring data-[state=active]:bg-background data-[state=active]:text-foreground inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm",
className
)}
{value}
{...$$restProps}
on:click
>
<slot />
</TabsPrimitive.Trigger>

21
src/lib/logger.ts Normal file
View file

@ -0,0 +1,21 @@
import winston from 'winston';
const { combine, timestamp, json, errors } = winston.format;
const logger = winston.createLogger({
levels: winston.config.syslog.levels,
format: combine(
errors({ stack: true }),
timestamp(),
json(),
),
transports: [
new winston.transports.Console({level: "debug"}),
new winston.transports.File({
filename: 'logs/app.log',
level: 'debug'
})
]
});
export default logger;

4
src/lib/prismaClient.ts Normal file
View file

@ -0,0 +1,4 @@
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
export default prisma;

11
src/lib/redisClient.ts Normal file
View file

@ -0,0 +1,11 @@
import { createClient } from 'redis';
const client = await createClient({
url: process.env.REDIS_URL || 'redis://localhost:6379'
});
client.on('error', (err) => console.error('Redis Error:', err));
await client.connect();
export default client;

View file

@ -0,0 +1,82 @@
import { type Actions } from '@sveltejs/kit';
import prismaClient from '$lib/prismaClient';
import * as argon2 from 'argon2';
import { redirect, error } from '@sveltejs/kit';
import logger from '$lib/logger';
export const actions: Actions = {
login: async ({request}) => {
const formData = await request.formData();
// @ts-ignore Can't be empty
const username = formData.get('username').toString();
// @ts-ignore Can't be empty
const password = formData.get('password').toString();
const user = await prismaClient.user.findFirst({
where: {
username: username,
}
});
if (user == null) {
return error(400, {message: "Nom d'utilisateur ou mot de passe invalide."});
}
try {
// @ts-ignore Already checked for null
if (await argon2.verify(user.password, password)) {
logger.log("info", "saijdazji")
logger.log("debug", "saijdazji")
} else {
return error(400, {message: "Nom d'utilisateur ou mot de passe invalide."});
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (e) {
return error(500, {message: "Erreur interne."})
}
},
register: async ({request}) => {
const formData = await request.formData();
// @ts-ignore Can't be empty
const username = formData.get('username').toString();
// @ts-ignore Can't be empty
const email = formData.get('email').toString();
// @ts-ignore Can't be empty
const password = formData.get('password').toString();
const user = await prismaClient.user.findFirst({
where: {
OR: [
{
email: email
},
{
username: username
}
]
}
});
if (user != null) {
return error(400, { message: "Un compte avec cette email ou nom d'utilisateur éxiste déjà." });
}
const hash = await argon2.hash(password);
await prismaClient.user.create({
data: {
email: email,
username: username,
name: "",
surname: "",
password: hash,
}
});
return redirect(302, "/chat");
}
}

View file

@ -3,26 +3,70 @@
import { Button } from "$lib/components/ui/button";
import { Input } from "$lib/components/ui/input";
import * as Card from "$lib/components/ui/card";
import { enhance } from '$app/forms';
import * as Tabs from "$lib/components/ui/tabs";
</script>
<div class="w-full h-full flex justify-center items-center">
<Card.Root class="w-[450px]">
<Tabs.Root value="login" class="w-[450px]">
<Tabs.List class="grid w-full grid-cols-2">
<Tabs.Trigger value="login">Se connecter</Tabs.Trigger>
<Tabs.Trigger value="register">S'inscrire</Tabs.Trigger>
</Tabs.List>
<Tabs.Content value="login">
<Card.Root>
<Card.Header>
<Card.Title>🌳</Card.Title>
<Card.Description>Un chat collaboratif</Card.Description>
<Card.Title>🌳 - Arbres</Card.Title>
<Card.Description>Connectez vous pour chatter!</Card.Description>
</Card.Header>
<form method="POST" action="?/login" use:enhance>
<Card.Content>
<div class="grid w-full max-w-sm items-center gap-1.5">
<Label for="username">Nom d'utilisateur</Label>
<Input type="text" id="username" />
<Input type="text" name="username" id="username" />
</div>
<div class="pt-4 grid w-full max-w-sm items-center gap-1.5">
<Label for="password">Mot de passe</Label>
<Input type="password" id="password" />
<Input type="password" name="password" id="password" />
</div>
</Card.Content>
<Card.Footer>
<Button>Se connecter</Button>
<Button type="submit">Se connecter</Button>
</Card.Footer>
</form>
</Card.Root>
</Tabs.Content>
<Tabs.Content value="register">
<Card.Root>
<Card.Header>
<Card.Title>🌳 - Arbres</Card.Title>
<Card.Description>Inscrivez-vous pour chatter!</Card.Description>
</Card.Header>
<form method="POST" action="?/register" use:enhance>
<Card.Content>
<div class="grid w-full max-w-sm items-center gap-1.5">
<Label for="username">Nom d'utilisateur</Label>
<Input type="text" name="username" id="username" />
</div>
<div class="pt-4 grid w-full max-w-sm items-center gap-1.5">
<Label for="email">Adresse email</Label>
<Input type="text" name="email" id="email" />
</div>
<div class="pt-4 grid w-full max-w-sm items-center gap-1.5">
<Label for="password">Mot de passe</Label>
<Input type="password" name="password" id="password" />
</div>
</Card.Content>
<Card.Footer>
<Button type="submit">S'inscrire</Button>
</Card.Footer>
</form>
</Card.Root>
</Tabs.Content>
</Tabs.Root>
</div>

View file

@ -0,0 +1,124 @@
import { json } from '@sveltejs/kit';
import prisma from '$lib/prismaClient';
import redisClient from '$lib/redisClient';
// 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, NX: true}); // 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: 300, NX: true });
return json(canalData);
} catch (err) {
console.error(err);
return json({ error: 'Erreur lors de la mise à jour du canal' }, { status: 500 });
}
}

View file

@ -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 lID et le pseudo de lutilisateur
},
},
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 dun 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 dun 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;
}
}

View file

@ -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 });
}
}

View file

@ -0,0 +1,108 @@
import { json } from '@sveltejs/kit';
import redisClient from '$lib/redisClient';
import prisma from '$lib/prismaClient';
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 });
}
}
export async function POST({ request }) {
const { pseudo, nom, prenom, email, password } = await request.json();
try {
const user = await prisma.user.create({
data: {
pseudo,
nom,
prenom,
email,
password,
},
});
// Mettre le nouvel utilisateur dans le cache
await redisClient.set(`user:${user.id}`, JSON.stringify(user), { EX: 3600 });
return json(user, { status: 201 });
} catch (err) {
console.error(err);
return json({ error: 'Erreur lors de la création de lutilisateur' }, { status: 500 });
}
}
// Mettre à jour un utilisateur avec PUT
export async function PUT({ params, request }) {
const userId = parseInt(params.id);
const { pseudo, nom, prenom, email, password } = await request.json(); // Assurez-vous d'envoyer tous les champs nécessaires dans le body
try {
// Mettre à jour l'utilisateur dans la base de données
const updatedUser = await prisma.user.update({
where: { id: userId },
data: {
pseudo,
nom,
prenom,
email,
password, // Attention à ne pas oublier de sécuriser le mot de passe avec bcrypt ou une autre méthode
},
});
// Mettre à jour l'utilisateur dans le cache Redis
await redisClient.set(`user:${userId}`, JSON.stringify(updatedUser), 'EX', 3600); // Cache pendant 1 heure (3600 secondes)
return json(updatedUser);
} catch (err) {
console.error(err);
return json({ error: 'Erreur lors de la mise à jour de lutilisateur' }, { status: 500 });
}
}
export async function DELETE({ params }) {
const userId = parseInt(params.id);
try {
await prisma.user.delete({
where: { id: userId },
});
// Supprimer l'utilisateur du cache Redis
await redisClient.del(`user:${userId}`);
return json({ message: 'Utilisateur supprimé avec succès' });
} catch (err) {
console.error(err);
return json({ error: 'Erreur lors de la suppression de lutilisateur' }, { status: 500 });
}
}

View file

@ -0,0 +1,27 @@
// src/routes/api/users/+server.js
import { json } from '@sveltejs/kit';
import redisClient from '$lib/redisClient';
import prisma from '$lib/prismaClient';
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 });
}
}