feat: phase 1 — auth + user endpoints, Prisma v7 adapter, DB init
This commit is contained in:
274
backend/package-lock.json
generated
274
backend/package-lock.json
generated
@@ -9,6 +9,7 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@prisma/adapter-pg": "^7.5.0",
|
||||||
"@prisma/client": "^7.5.0",
|
"@prisma/client": "^7.5.0",
|
||||||
"argon2": "^0.44.0",
|
"argon2": "^0.44.0",
|
||||||
"cookie-parser": "^1.4.7",
|
"cookie-parser": "^1.4.7",
|
||||||
@@ -16,6 +17,7 @@
|
|||||||
"express": "^5.2.1",
|
"express": "^5.2.1",
|
||||||
"jsonwebtoken": "^9.0.3",
|
"jsonwebtoken": "^9.0.3",
|
||||||
"multer": "^2.1.1",
|
"multer": "^2.1.1",
|
||||||
|
"pg": "^8.20.0",
|
||||||
"zod": "^4.3.6"
|
"zod": "^4.3.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -24,6 +26,7 @@
|
|||||||
"@types/express": "^5.0.6",
|
"@types/express": "^5.0.6",
|
||||||
"@types/jsonwebtoken": "^9.0.10",
|
"@types/jsonwebtoken": "^9.0.10",
|
||||||
"@types/multer": "^2.1.0",
|
"@types/multer": "^2.1.0",
|
||||||
|
"@types/pg": "^8.20.0",
|
||||||
"nodemon": "^3.1.14",
|
"nodemon": "^3.1.14",
|
||||||
"prisma": "^7.5.0",
|
"prisma": "^7.5.0",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
@@ -180,6 +183,77 @@
|
|||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@prisma/adapter-pg": {
|
||||||
|
"version": "7.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/adapter-pg/-/adapter-pg-7.5.0.tgz",
|
||||||
|
"integrity": "sha512-EJx7OLULahcC3IjJgdx2qRDNCT+ToY2v66UkeETMCLhNOTgqVzRzYvOEphY7Zp0eHyzfkC33Edd/qqeadf9R4A==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/driver-adapter-utils": "7.5.0",
|
||||||
|
"@types/pg": "8.11.11",
|
||||||
|
"pg": "^8.16.3",
|
||||||
|
"postgres-array": "3.0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@prisma/adapter-pg/node_modules/@types/pg": {
|
||||||
|
"version": "8.11.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.11.11.tgz",
|
||||||
|
"integrity": "sha512-kGT1qKM8wJQ5qlawUrEkXgvMSXoV213KfMGXcwfDwUIfUHXqXYXOfS1nE1LINRJVVVx5wCm70XnFlMHaIcQAfw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*",
|
||||||
|
"pg-protocol": "*",
|
||||||
|
"pg-types": "^4.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@prisma/adapter-pg/node_modules/pg-types": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-o2XFanIMy/3+mThw69O8d4n1E5zsLhdO+OPqswezu7Z5ekP4hYDqlDjlmOpYMbzY2Br0ufCwJLdDIXeNVwcWFg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"pg-int8": "1.0.1",
|
||||||
|
"pg-numeric": "1.0.2",
|
||||||
|
"postgres-array": "~3.0.1",
|
||||||
|
"postgres-bytea": "~3.0.0",
|
||||||
|
"postgres-date": "~2.1.0",
|
||||||
|
"postgres-interval": "^3.0.0",
|
||||||
|
"postgres-range": "^1.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@prisma/adapter-pg/node_modules/postgres-bytea": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"obuf": "~1.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@prisma/adapter-pg/node_modules/postgres-date": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@prisma/adapter-pg/node_modules/postgres-interval": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@prisma/client": {
|
"node_modules/@prisma/client": {
|
||||||
"version": "7.5.0",
|
"version": "7.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-7.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-7.5.0.tgz",
|
||||||
@@ -227,7 +301,6 @@
|
|||||||
"version": "7.5.0",
|
"version": "7.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-7.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-7.5.0.tgz",
|
||||||
"integrity": "sha512-163+nffny0JoPEkDhfNco0vcuT3ymIJc9+WX7MHSQhfkeKUmKe9/wqvGk5SjppT93DtBjVwr5HPJYlXbzm6qtg==",
|
"integrity": "sha512-163+nffny0JoPEkDhfNco0vcuT3ymIJc9+WX7MHSQhfkeKUmKe9/wqvGk5SjppT93DtBjVwr5HPJYlXbzm6qtg==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "Apache-2.0"
|
"license": "Apache-2.0"
|
||||||
},
|
},
|
||||||
"node_modules/@prisma/dev": {
|
"node_modules/@prisma/dev": {
|
||||||
@@ -256,6 +329,15 @@
|
|||||||
"zeptomatch": "2.1.0"
|
"zeptomatch": "2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@prisma/driver-adapter-utils": {
|
||||||
|
"version": "7.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/driver-adapter-utils/-/driver-adapter-utils-7.5.0.tgz",
|
||||||
|
"integrity": "sha512-B79N/amgV677mFesFDBAdrW0OIaqawap9E0sjgLBtzIz2R3hIMS1QB8mLZuUEiS4q5Y8Oh3I25Kw4SLxMypk9Q==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/debug": "7.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@prisma/engines": {
|
"node_modules/@prisma/engines": {
|
||||||
"version": "7.5.0",
|
"version": "7.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-7.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-7.5.0.tgz",
|
||||||
@@ -489,12 +571,23 @@
|
|||||||
"version": "25.5.0",
|
"version": "25.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz",
|
||||||
"integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==",
|
"integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~7.18.0"
|
"undici-types": "~7.18.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/pg": {
|
||||||
|
"version": "8.20.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.20.0.tgz",
|
||||||
|
"integrity": "sha512-bEPFOaMAHTEP1EzpvHTbmwR8UsFyHSKsRisLIHVMXnpNefSbGA1bD6CVy+qKjGSqmZqNqBDV2azOBo8TgkcVow==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*",
|
||||||
|
"pg-protocol": "*",
|
||||||
|
"pg-types": "^2.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/qs": {
|
"node_modules/@types/qs": {
|
||||||
"version": "6.15.0",
|
"version": "6.15.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.0.tgz",
|
||||||
@@ -2111,6 +2204,12 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/obuf": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/ohash": {
|
"node_modules/ohash": {
|
||||||
"version": "2.0.11",
|
"version": "2.0.11",
|
||||||
"resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz",
|
"resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz",
|
||||||
@@ -2181,6 +2280,113 @@
|
|||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/pg": {
|
||||||
|
"version": "8.20.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pg/-/pg-8.20.0.tgz",
|
||||||
|
"integrity": "sha512-ldhMxz2r8fl/6QkXnBD3CR9/xg694oT6DZQ2s6c/RI28OjtSOpxnPrUCGOBJ46RCUxcWdx3p6kw/xnDHjKvaRA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"pg-connection-string": "^2.12.0",
|
||||||
|
"pg-pool": "^3.13.0",
|
||||||
|
"pg-protocol": "^1.13.0",
|
||||||
|
"pg-types": "2.2.0",
|
||||||
|
"pgpass": "1.0.5"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 16.0.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"pg-cloudflare": "^1.3.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"pg-native": ">=3.0.1"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"pg-native": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pg-cloudflare": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"node_modules/pg-connection-string": {
|
||||||
|
"version": "2.12.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.12.0.tgz",
|
||||||
|
"integrity": "sha512-U7qg+bpswf3Cs5xLzRqbXbQl85ng0mfSV/J0nnA31MCLgvEaAo7CIhmeyrmJpOr7o+zm0rXK+hNnT5l9RHkCkQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/pg-int8": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pg-numeric": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/pg-numeric/-/pg-numeric-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==",
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pg-pool": {
|
||||||
|
"version": "3.13.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.13.0.tgz",
|
||||||
|
"integrity": "sha512-gB+R+Xud1gLFuRD/QgOIgGOBE2KCQPaPwkzBBGC9oG69pHTkhQeIuejVIk3/cnDyX39av2AxomQiyPT13WKHQA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"pg": ">=8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pg-protocol": {
|
||||||
|
"version": "1.13.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.13.0.tgz",
|
||||||
|
"integrity": "sha512-zzdvXfS6v89r6v7OcFCHfHlyG/wvry1ALxZo4LqgUoy7W9xhBDMaqOuMiF3qEV45VqsN6rdlcehHrfDtlCPc8w==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/pg-types": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"pg-int8": "1.0.1",
|
||||||
|
"postgres-array": "~2.0.0",
|
||||||
|
"postgres-bytea": "~1.0.0",
|
||||||
|
"postgres-date": "~1.0.4",
|
||||||
|
"postgres-interval": "^1.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pg-types/node_modules/postgres-array": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pgpass": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"split2": "^4.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/picomatch": {
|
"node_modules/picomatch": {
|
||||||
"version": "2.3.2",
|
"version": "2.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
|
||||||
@@ -2220,6 +2426,51 @@
|
|||||||
"url": "https://github.com/sponsors/porsager"
|
"url": "https://github.com/sponsors/porsager"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/postgres-array": {
|
||||||
|
"version": "3.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-3.0.4.tgz",
|
||||||
|
"integrity": "sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/postgres-bytea": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/postgres-date": {
|
||||||
|
"version": "1.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
|
||||||
|
"integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/postgres-interval": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"xtend": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/postgres-range": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/postgres-range/-/postgres-range-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/prisma": {
|
"node_modules/prisma": {
|
||||||
"version": "7.5.0",
|
"version": "7.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/prisma/-/prisma-7.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/prisma/-/prisma-7.5.0.tgz",
|
||||||
@@ -2677,6 +2928,15 @@
|
|||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/split2": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/sqlstring": {
|
"node_modules/sqlstring": {
|
||||||
"version": "2.3.3",
|
"version": "2.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz",
|
||||||
@@ -2864,7 +3124,6 @@
|
|||||||
"version": "7.18.2",
|
"version": "7.18.2",
|
||||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
|
||||||
"integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
|
"integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/unpipe": {
|
"node_modules/unpipe": {
|
||||||
@@ -2934,6 +3193,15 @@
|
|||||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/xtend": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/yn": {
|
"node_modules/yn": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"description": "",
|
"description": "",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@prisma/adapter-pg": "^7.5.0",
|
||||||
"@prisma/client": "^7.5.0",
|
"@prisma/client": "^7.5.0",
|
||||||
"argon2": "^0.44.0",
|
"argon2": "^0.44.0",
|
||||||
"cookie-parser": "^1.4.7",
|
"cookie-parser": "^1.4.7",
|
||||||
@@ -22,6 +23,7 @@
|
|||||||
"express": "^5.2.1",
|
"express": "^5.2.1",
|
||||||
"jsonwebtoken": "^9.0.3",
|
"jsonwebtoken": "^9.0.3",
|
||||||
"multer": "^2.1.1",
|
"multer": "^2.1.1",
|
||||||
|
"pg": "^8.20.0",
|
||||||
"zod": "^4.3.6"
|
"zod": "^4.3.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -30,6 +32,7 @@
|
|||||||
"@types/express": "^5.0.6",
|
"@types/express": "^5.0.6",
|
||||||
"@types/jsonwebtoken": "^9.0.10",
|
"@types/jsonwebtoken": "^9.0.10",
|
||||||
"@types/multer": "^2.1.0",
|
"@types/multer": "^2.1.0",
|
||||||
|
"@types/pg": "^8.20.0",
|
||||||
"nodemon": "^3.1.14",
|
"nodemon": "^3.1.14",
|
||||||
"prisma": "^7.5.0",
|
"prisma": "^7.5.0",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
|
|||||||
206
backend/prisma/migrations/20260326033603_init/migration.sql
Normal file
206
backend/prisma/migrations/20260326033603_init/migration.sql
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
-- CreateEnum
|
||||||
|
CREATE TYPE "Role" AS ENUM ('USER', 'COACH', 'ADMIN');
|
||||||
|
|
||||||
|
-- CreateEnum
|
||||||
|
CREATE TYPE "Difficulty" AS ENUM ('BEGINNER', 'INTERMEDIATE', 'ADVANCED');
|
||||||
|
|
||||||
|
-- CreateEnum
|
||||||
|
CREATE TYPE "GroupRole" AS ENUM ('MEMBER', 'COACH', 'ADMIN');
|
||||||
|
|
||||||
|
-- CreateEnum
|
||||||
|
CREATE TYPE "FriendStatus" AS ENUM ('PENDING', 'ACCEPTED', 'REJECTED');
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "users" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"username" TEXT NOT NULL,
|
||||||
|
"email" TEXT NOT NULL,
|
||||||
|
"password" TEXT NOT NULL,
|
||||||
|
"role" "Role" NOT NULL DEFAULT 'USER',
|
||||||
|
"avatar" TEXT,
|
||||||
|
"bio" TEXT,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "users_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "exercises" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"description" TEXT,
|
||||||
|
"difficulty" "Difficulty" NOT NULL DEFAULT 'BEGINNER',
|
||||||
|
"modelPath" TEXT,
|
||||||
|
"muscleGroups" TEXT[],
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "exercises_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "programs" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"description" TEXT,
|
||||||
|
"isPublic" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"authorId" TEXT NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "programs_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "program_exercises" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"sets" INTEGER NOT NULL,
|
||||||
|
"reps" INTEGER,
|
||||||
|
"durationSec" INTEGER,
|
||||||
|
"order" INTEGER NOT NULL DEFAULT 0,
|
||||||
|
"programId" TEXT NOT NULL,
|
||||||
|
"exerciseId" TEXT NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "program_exercises_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "histories" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"date" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"notes" TEXT,
|
||||||
|
"userId" TEXT NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "histories_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "history_entries" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"sets" INTEGER NOT NULL,
|
||||||
|
"reps" INTEGER,
|
||||||
|
"weightKg" DOUBLE PRECISION,
|
||||||
|
"durationSec" INTEGER,
|
||||||
|
"historyId" TEXT NOT NULL,
|
||||||
|
"exerciseId" TEXT NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "history_entries_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "groups" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"description" TEXT,
|
||||||
|
"isPublic" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "groups_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "group_members" (
|
||||||
|
"role" "GroupRole" NOT NULL DEFAULT 'MEMBER',
|
||||||
|
"joinedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"groupId" TEXT NOT NULL,
|
||||||
|
"userId" TEXT NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "group_members_pkey" PRIMARY KEY ("groupId","userId")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "group_programs" (
|
||||||
|
"addedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"groupId" TEXT NOT NULL,
|
||||||
|
"programId" TEXT NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "group_programs_pkey" PRIMARY KEY ("groupId","programId")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "friend_requests" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"status" "FriendStatus" NOT NULL DEFAULT 'PENDING',
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"senderId" TEXT NOT NULL,
|
||||||
|
"receiverId" TEXT NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "friend_requests_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "rewards" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"description" TEXT NOT NULL,
|
||||||
|
"iconPath" TEXT,
|
||||||
|
"condition" TEXT NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "rewards_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "user_rewards" (
|
||||||
|
"earnedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"userId" TEXT NOT NULL,
|
||||||
|
"rewardId" TEXT NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "user_rewards_pkey" PRIMARY KEY ("userId","rewardId")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "users_username_key" ON "users"("username");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "users_email_key" ON "users"("email");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "program_exercises_programId_exerciseId_key" ON "program_exercises"("programId", "exerciseId");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "friend_requests_senderId_receiverId_key" ON "friend_requests"("senderId", "receiverId");
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "programs" ADD CONSTRAINT "programs_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "program_exercises" ADD CONSTRAINT "program_exercises_programId_fkey" FOREIGN KEY ("programId") REFERENCES "programs"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "program_exercises" ADD CONSTRAINT "program_exercises_exerciseId_fkey" FOREIGN KEY ("exerciseId") REFERENCES "exercises"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "histories" ADD CONSTRAINT "histories_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "history_entries" ADD CONSTRAINT "history_entries_historyId_fkey" FOREIGN KEY ("historyId") REFERENCES "histories"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "history_entries" ADD CONSTRAINT "history_entries_exerciseId_fkey" FOREIGN KEY ("exerciseId") REFERENCES "exercises"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "group_members" ADD CONSTRAINT "group_members_groupId_fkey" FOREIGN KEY ("groupId") REFERENCES "groups"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "group_members" ADD CONSTRAINT "group_members_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "group_programs" ADD CONSTRAINT "group_programs_groupId_fkey" FOREIGN KEY ("groupId") REFERENCES "groups"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "group_programs" ADD CONSTRAINT "group_programs_programId_fkey" FOREIGN KEY ("programId") REFERENCES "programs"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "friend_requests" ADD CONSTRAINT "friend_requests_senderId_fkey" FOREIGN KEY ("senderId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "friend_requests" ADD CONSTRAINT "friend_requests_receiverId_fkey" FOREIGN KEY ("receiverId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "user_rewards" ADD CONSTRAINT "user_rewards_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "user_rewards" ADD CONSTRAINT "user_rewards_rewardId_fkey" FOREIGN KEY ("rewardId") REFERENCES "rewards"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
3
backend/prisma/migrations/migration_lock.toml
Normal file
3
backend/prisma/migrations/migration_lock.toml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Please do not edit this file manually
|
||||||
|
# It should be added in your version-control system (e.g., Git)
|
||||||
|
provider = "postgresql"
|
||||||
@@ -4,7 +4,6 @@ generator client {
|
|||||||
|
|
||||||
datasource db {
|
datasource db {
|
||||||
provider = "postgresql"
|
provider = "postgresql"
|
||||||
url = env("DATABASE_URL")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Enums ───────────────────────────────────────────────────────────────────
|
// ─── Enums ───────────────────────────────────────────────────────────────────
|
||||||
|
|||||||
93
backend/src/controllers/auth.controller.ts
Normal file
93
backend/src/controllers/auth.controller.ts
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
import type { Response } from "express";
|
||||||
|
import argon2 from "argon2";
|
||||||
|
import jwt from "jsonwebtoken";
|
||||||
|
import { prisma } from "../index";
|
||||||
|
import type { AppRequest } from "../types/context";
|
||||||
|
import { loginSchema, registerSchema } from "../validators/auth.validators";
|
||||||
|
|
||||||
|
const COOKIE_OPTIONS = {
|
||||||
|
httpOnly: true,
|
||||||
|
secure: process.env.NODE_ENV === "production",
|
||||||
|
sameSite: "lax" as const,
|
||||||
|
maxAge: 7 * 24 * 60 * 60 * 1000, // 7 jours
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function register(req: AppRequest, res: Response): Promise<void> {
|
||||||
|
const parsed = registerSchema.safeParse(req.body);
|
||||||
|
if (!parsed.success) {
|
||||||
|
res.status(400).json({ message: parsed.error.issues[0].message });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { username, email, password } = parsed.data;
|
||||||
|
|
||||||
|
const existing = await prisma.user.findFirst({
|
||||||
|
where: { OR: [{ email }, { username }] },
|
||||||
|
});
|
||||||
|
if (existing) {
|
||||||
|
res.status(409).json({ message: "Email ou username déjà utilisé." });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hashed = await argon2.hash(password);
|
||||||
|
const user = await prisma.user.create({
|
||||||
|
data: { username, email, password: hashed },
|
||||||
|
});
|
||||||
|
|
||||||
|
const token = jwt.sign(
|
||||||
|
{ id: user.id, email: user.email, role: user.role },
|
||||||
|
process.env.JWT_SECRET as string,
|
||||||
|
{ expiresIn: "7d" }
|
||||||
|
);
|
||||||
|
|
||||||
|
res.cookie("token", token, COOKIE_OPTIONS);
|
||||||
|
res.status(201).json({ id: user.id, username: user.username, email: user.email, role: user.role });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function login(req: AppRequest, res: Response): Promise<void> {
|
||||||
|
const parsed = loginSchema.safeParse(req.body);
|
||||||
|
if (!parsed.success) {
|
||||||
|
res.status(400).json({ message: parsed.error.issues[0].message });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { email, password } = parsed.data;
|
||||||
|
|
||||||
|
const user = await prisma.user.findUnique({ where: { email } });
|
||||||
|
if (!user) {
|
||||||
|
res.status(401).json({ message: "Identifiants invalides." });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const valid = await argon2.verify(user.password, password);
|
||||||
|
if (!valid) {
|
||||||
|
res.status(401).json({ message: "Identifiants invalides." });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const token = jwt.sign(
|
||||||
|
{ id: user.id, email: user.email, role: user.role },
|
||||||
|
process.env.JWT_SECRET as string,
|
||||||
|
{ expiresIn: "7d" }
|
||||||
|
);
|
||||||
|
|
||||||
|
res.cookie("token", token, COOKIE_OPTIONS);
|
||||||
|
res.json({ id: user.id, username: user.username, email: user.email, role: user.role });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function logout(_req: AppRequest, res: Response): void {
|
||||||
|
res.clearCookie("token");
|
||||||
|
res.json({ message: "Déconnecté." });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function me(req: AppRequest, res: Response): Promise<void> {
|
||||||
|
const user = await prisma.user.findUnique({
|
||||||
|
where: { id: req.user!.id },
|
||||||
|
select: { id: true, username: true, email: true, role: true, avatar: true, bio: true, createdAt: true },
|
||||||
|
});
|
||||||
|
if (!user) {
|
||||||
|
res.status(404).json({ message: "Utilisateur introuvable." });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.json(user);
|
||||||
|
}
|
||||||
41
backend/src/controllers/user.controller.ts
Normal file
41
backend/src/controllers/user.controller.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import type { Response } from "express";
|
||||||
|
import { prisma } from "../index";
|
||||||
|
import type { AppRequest } from "../types/context";
|
||||||
|
import { updateUserSchema } from "../validators/user.validators";
|
||||||
|
|
||||||
|
export async function getMe(req: AppRequest, res: Response): Promise<void> {
|
||||||
|
const user = await prisma.user.findUnique({
|
||||||
|
where: { id: req.user!.id },
|
||||||
|
select: { id: true, username: true, email: true, role: true, avatar: true, bio: true, createdAt: true },
|
||||||
|
});
|
||||||
|
if (!user) {
|
||||||
|
res.status(404).json({ message: "Utilisateur introuvable." });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.json(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateMe(req: AppRequest, res: Response): Promise<void> {
|
||||||
|
const parsed = updateUserSchema.safeParse(req.body);
|
||||||
|
if (!parsed.success) {
|
||||||
|
res.status(400).json({ message: parsed.error.issues[0].message });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parsed.data.username) {
|
||||||
|
const taken = await prisma.user.findFirst({
|
||||||
|
where: { username: parsed.data.username, NOT: { id: req.user!.id } },
|
||||||
|
});
|
||||||
|
if (taken) {
|
||||||
|
res.status(409).json({ message: "Username déjà utilisé." });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await prisma.user.update({
|
||||||
|
where: { id: req.user!.id },
|
||||||
|
data: parsed.data,
|
||||||
|
select: { id: true, username: true, email: true, role: true, avatar: true, bio: true },
|
||||||
|
});
|
||||||
|
res.json(user);
|
||||||
|
}
|
||||||
@@ -1,10 +1,15 @@
|
|||||||
|
import "dotenv/config";
|
||||||
import express from "express";
|
import express from "express";
|
||||||
import cors from "cors";
|
import cors from "cors";
|
||||||
import cookieParser from "cookie-parser";
|
import cookieParser from "cookie-parser";
|
||||||
import { PrismaClient } from "@prisma/client";
|
import { PrismaClient } from "@prisma/client";
|
||||||
|
import { PrismaPg } from "@prisma/adapter-pg";
|
||||||
|
import authRoutes from "./routes/auth.routes";
|
||||||
|
import userRoutes from "./routes/user.routes";
|
||||||
|
|
||||||
|
const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL });
|
||||||
const app = express();
|
const app = express();
|
||||||
const prisma = new PrismaClient();
|
const prisma = new PrismaClient({ adapter });
|
||||||
|
|
||||||
const PORT = process.env.PORT ?? 4000;
|
const PORT = process.env.PORT ?? 4000;
|
||||||
const CLIENT_URL = process.env.CLIENT_URL ?? "http://localhost:5173";
|
const CLIENT_URL = process.env.CLIENT_URL ?? "http://localhost:5173";
|
||||||
@@ -15,23 +20,18 @@ if (!process.env.JWT_SECRET) {
|
|||||||
|
|
||||||
// ─── Middlewares ──────────────────────────────────────────────────────────────
|
// ─── Middlewares ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
app.use(
|
app.use(cors({ origin: CLIENT_URL, credentials: true }));
|
||||||
cors({
|
|
||||||
origin: CLIENT_URL,
|
|
||||||
credentials: true,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
app.use(cookieParser());
|
app.use(cookieParser());
|
||||||
|
|
||||||
// ─── Static uploads ──────────────────────────────────────────────────────────
|
// ─── Static uploads ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
app.use("/uploads", express.static("uploads"));
|
app.use("/uploads", express.static("uploads"));
|
||||||
|
|
||||||
// ─── Routes ──────────────────────────────────────────────────────────────────
|
// ─── Routes ───────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
// app.use("/api/auth", authRoutes);
|
app.use("/api/auth", authRoutes);
|
||||||
// app.use("/api/users", userRoutes);
|
app.use("/api/users", userRoutes);
|
||||||
// app.use("/api/exercises", exerciseRoutes);
|
// app.use("/api/exercises", exerciseRoutes);
|
||||||
// app.use("/api/programs", programRoutes);
|
// app.use("/api/programs", programRoutes);
|
||||||
// app.use("/api/groups", groupRoutes);
|
// app.use("/api/groups", groupRoutes);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { Response, NextFunction } from "express";
|
import type { Response, NextFunction } from "express";
|
||||||
import jwt from "jsonwebtoken";
|
import jwt from "jsonwebtoken";
|
||||||
import type { AppRequest, AuthPayload } from "../types/context.js";
|
import type { AppRequest, AuthPayload } from "../types/context";
|
||||||
|
|
||||||
export function requireAuth(req: AppRequest, res: Response, next: NextFunction): void {
|
export function requireAuth(req: AppRequest, res: Response, next: NextFunction): void {
|
||||||
const token = req.cookies?.token as string | undefined;
|
const token = req.cookies?.token as string | undefined;
|
||||||
|
|||||||
12
backend/src/routes/auth.routes.ts
Normal file
12
backend/src/routes/auth.routes.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { Router } from "express";
|
||||||
|
import { login, logout, me, register } from "../controllers/auth.controller";
|
||||||
|
import { requireAuth } from "../middlewares/auth";
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
router.post("/register", register);
|
||||||
|
router.post("/login", login);
|
||||||
|
router.post("/logout", logout);
|
||||||
|
router.get("/me", requireAuth, me);
|
||||||
|
|
||||||
|
export default router;
|
||||||
10
backend/src/routes/user.routes.ts
Normal file
10
backend/src/routes/user.routes.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { Router } from "express";
|
||||||
|
import { getMe, updateMe } from "../controllers/user.controller";
|
||||||
|
import { requireAuth } from "../middlewares/auth";
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
router.get("/me", requireAuth, getMe);
|
||||||
|
router.patch("/me", requireAuth, updateMe);
|
||||||
|
|
||||||
|
export default router;
|
||||||
15
backend/src/validators/auth.validators.ts
Normal file
15
backend/src/validators/auth.validators.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
export const registerSchema = z.object({
|
||||||
|
username: z.string().min(3).max(20),
|
||||||
|
email: z.string().email(),
|
||||||
|
password: z.string().min(8),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const loginSchema = z.object({
|
||||||
|
email: z.string().email(),
|
||||||
|
password: z.string().min(1),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type RegisterInput = z.infer<typeof registerSchema>;
|
||||||
|
export type LoginInput = z.infer<typeof loginSchema>;
|
||||||
9
backend/src/validators/user.validators.ts
Normal file
9
backend/src/validators/user.validators.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
export const updateUserSchema = z.object({
|
||||||
|
username: z.string().min(3).max(20).optional(),
|
||||||
|
bio: z.string().max(300).optional(),
|
||||||
|
avatar: z.string().url().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type UpdateUserInput = z.infer<typeof updateUserSchema>;
|
||||||
Reference in New Issue
Block a user