Add services and auth
This commit is contained in:
parent
9b0b5dc040
commit
5dec454afb
46 changed files with 900 additions and 31 deletions
221
package-lock.json
generated
221
package-lock.json
generated
|
|
@ -11,10 +11,13 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@prisma/adapter-pg": "^7.2.0",
|
"@prisma/adapter-pg": "^7.2.0",
|
||||||
"@prisma/client": "^7.2.0",
|
"@prisma/client": "^7.2.0",
|
||||||
|
"argon2": "^0.44.0",
|
||||||
"fastify": "^5.6.2",
|
"fastify": "^5.6.2",
|
||||||
|
"jsonwebtoken": "^9.0.3",
|
||||||
"pg": "^8.16.3"
|
"pg": "^8.16.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/jsonwebtoken": "^9.0.10",
|
||||||
"@types/node": "^25.0.3",
|
"@types/node": "^25.0.3",
|
||||||
"@types/pg": "^8.16.0",
|
"@types/pg": "^8.16.0",
|
||||||
"dotenv": "^17.2.3",
|
"dotenv": "^17.2.3",
|
||||||
|
|
@ -103,6 +106,12 @@
|
||||||
"@electric-sql/pglite": "0.3.2"
|
"@electric-sql/pglite": "0.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@epic-web/invariant": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@epic-web/invariant/-/invariant-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@fastify/ajv-compiler": {
|
"node_modules/@fastify/ajv-compiler": {
|
||||||
"version": "4.0.5",
|
"version": "4.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-4.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-4.0.5.tgz",
|
||||||
|
|
@ -269,6 +278,15 @@
|
||||||
"node": ">=16"
|
"node": ">=16"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@phc/format": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@phc/format/-/format-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@pinojs/redact": {
|
"node_modules/@pinojs/redact": {
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz",
|
||||||
|
|
@ -494,6 +512,24 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/jsonwebtoken": {
|
||||||
|
"version": "9.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz",
|
||||||
|
"integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/ms": "*",
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/ms": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "25.0.3",
|
"version": "25.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.3.tgz",
|
||||||
|
|
@ -599,6 +635,22 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/argon2": {
|
||||||
|
"version": "0.44.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/argon2/-/argon2-0.44.0.tgz",
|
||||||
|
"integrity": "sha512-zHPGN3S55sihSQo0dBbK0A5qpi2R31z7HZDZnry3ifOyj8bZZnpZND2gpmhnRGO1V/d555RwBqIK5W4Mrmv3ig==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@phc/format": "^1.0.0",
|
||||||
|
"cross-env": "^10.0.0",
|
||||||
|
"node-addon-api": "^8.5.0",
|
||||||
|
"node-gyp-build": "^4.8.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.17.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/atomic-sleep": {
|
"node_modules/atomic-sleep": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz",
|
||||||
|
|
@ -628,6 +680,12 @@
|
||||||
"node": ">= 6.0.0"
|
"node": ">= 6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/buffer-equal-constant-time": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
|
||||||
|
"license": "BSD-3-Clause"
|
||||||
|
},
|
||||||
"node_modules/c12": {
|
"node_modules/c12": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/c12/-/c12-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/c12/-/c12-3.1.0.tgz",
|
||||||
|
|
@ -748,11 +806,27 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/cross-env": {
|
||||||
|
"version": "10.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-10.1.0.tgz",
|
||||||
|
"integrity": "sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@epic-web/invariant": "^1.0.0",
|
||||||
|
"cross-spawn": "^7.0.6"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"cross-env": "dist/bin/cross-env.js",
|
||||||
|
"cross-env-shell": "dist/bin/cross-env-shell.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/cross-spawn": {
|
"node_modules/cross-spawn": {
|
||||||
"version": "7.0.6",
|
"version": "7.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||||
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
|
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"path-key": "^3.1.0",
|
"path-key": "^3.1.0",
|
||||||
|
|
@ -837,6 +911,15 @@
|
||||||
"url": "https://dotenvx.com"
|
"url": "https://dotenvx.com"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/ecdsa-sig-formatter": {
|
||||||
|
"version": "1.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
|
||||||
|
"integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"safe-buffer": "^5.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/effect": {
|
"node_modules/effect": {
|
||||||
"version": "3.18.4",
|
"version": "3.18.4",
|
||||||
"resolved": "https://registry.npmjs.org/effect/-/effect-3.18.4.tgz",
|
"resolved": "https://registry.npmjs.org/effect/-/effect-3.18.4.tgz",
|
||||||
|
|
@ -1125,7 +1208,6 @@
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/jiti": {
|
"node_modules/jiti": {
|
||||||
|
|
@ -1163,6 +1245,49 @@
|
||||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/jsonwebtoken": {
|
||||||
|
"version": "9.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz",
|
||||||
|
"integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"jws": "^4.0.1",
|
||||||
|
"lodash.includes": "^4.3.0",
|
||||||
|
"lodash.isboolean": "^3.0.3",
|
||||||
|
"lodash.isinteger": "^4.0.4",
|
||||||
|
"lodash.isnumber": "^3.0.3",
|
||||||
|
"lodash.isplainobject": "^4.0.6",
|
||||||
|
"lodash.isstring": "^4.0.1",
|
||||||
|
"lodash.once": "^4.0.0",
|
||||||
|
"ms": "^2.1.1",
|
||||||
|
"semver": "^7.5.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12",
|
||||||
|
"npm": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jwa": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"buffer-equal-constant-time": "^1.0.1",
|
||||||
|
"ecdsa-sig-formatter": "1.0.11",
|
||||||
|
"safe-buffer": "^5.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jws": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"jwa": "^2.0.1",
|
||||||
|
"safe-buffer": "^5.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/light-my-request": {
|
"node_modules/light-my-request": {
|
||||||
"version": "6.6.0",
|
"version": "6.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-6.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-6.6.0.tgz",
|
||||||
|
|
@ -1217,6 +1342,48 @@
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/lodash.includes": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/lodash.isboolean": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/lodash.isinteger": {
|
||||||
|
"version": "4.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
|
||||||
|
"integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/lodash.isnumber": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/lodash.isplainobject": {
|
||||||
|
"version": "4.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
|
||||||
|
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/lodash.isstring": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/lodash.once": {
|
||||||
|
"version": "4.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
|
||||||
|
"integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/long": {
|
"node_modules/long": {
|
||||||
"version": "5.3.2",
|
"version": "5.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
|
||||||
|
|
@ -1247,6 +1414,12 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/ms": {
|
||||||
|
"version": "2.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
|
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/mysql2": {
|
"node_modules/mysql2": {
|
||||||
"version": "3.15.3",
|
"version": "3.15.3",
|
||||||
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.15.3.tgz",
|
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.15.3.tgz",
|
||||||
|
|
@ -1281,6 +1454,15 @@
|
||||||
"node": ">=8.0.0"
|
"node": ">=8.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/node-addon-api": {
|
||||||
|
"version": "8.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz",
|
||||||
|
"integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "^18 || ^20 || >= 21"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/node-fetch-native": {
|
"node_modules/node-fetch-native": {
|
||||||
"version": "1.6.7",
|
"version": "1.6.7",
|
||||||
"resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz",
|
"resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz",
|
||||||
|
|
@ -1288,6 +1470,17 @@
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/node-gyp-build": {
|
||||||
|
"version": "4.8.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz",
|
||||||
|
"integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"node-gyp-build": "bin.js",
|
||||||
|
"node-gyp-build-optional": "optional.js",
|
||||||
|
"node-gyp-build-test": "build-test.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/nypm": {
|
"node_modules/nypm": {
|
||||||
"version": "0.6.2",
|
"version": "0.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.2.tgz",
|
||||||
|
|
@ -1328,7 +1521,6 @@
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
||||||
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
|
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
|
|
@ -1760,6 +1952,26 @@
|
||||||
"integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
|
"integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/safe-buffer": {
|
||||||
|
"version": "5.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||||
|
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/safe-regex2": {
|
"node_modules/safe-regex2": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-5.0.0.tgz",
|
||||||
|
|
@ -1847,7 +2059,6 @@
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||||
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
|
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"shebang-regex": "^3.0.0"
|
"shebang-regex": "^3.0.0"
|
||||||
|
|
@ -1860,7 +2071,6 @@
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
|
||||||
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
|
|
@ -2046,7 +2256,6 @@
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
|
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"isexe": "^2.0.0"
|
"isexe": "^2.0.0"
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
"start": "npx tsc && node --env-file=.env dist/index.js"
|
"start": "npx tsc && node --env-file=.env dist/index.js"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/jsonwebtoken": "^9.0.10",
|
||||||
"@types/node": "^25.0.3",
|
"@types/node": "^25.0.3",
|
||||||
"@types/pg": "^8.16.0",
|
"@types/pg": "^8.16.0",
|
||||||
"dotenv": "^17.2.3",
|
"dotenv": "^17.2.3",
|
||||||
|
|
@ -25,7 +26,9 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@prisma/adapter-pg": "^7.2.0",
|
"@prisma/adapter-pg": "^7.2.0",
|
||||||
"@prisma/client": "^7.2.0",
|
"@prisma/client": "^7.2.0",
|
||||||
|
"argon2": "^0.44.0",
|
||||||
"fastify": "^5.6.2",
|
"fastify": "^5.6.2",
|
||||||
|
"jsonwebtoken": "^9.0.3",
|
||||||
"pg": "^8.16.3"
|
"pg": "^8.16.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
56
src/controllers/auth/auth.ts
Normal file
56
src/controllers/auth/auth.ts
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
import { type FastifyReply, type FastifyRequest } from "fastify";
|
||||||
|
import type {
|
||||||
|
ILoginRequest,
|
||||||
|
IRegisterResponseError,
|
||||||
|
IRegisterResponseSuccess,
|
||||||
|
IRegisterRequest,
|
||||||
|
ILoginResponseError,
|
||||||
|
ILoginResponseSuccess,
|
||||||
|
} from "./types.js";
|
||||||
|
import { loginUser, registerUser } from "../../services/auth/auth.js";
|
||||||
|
|
||||||
|
const postRegister = async (request: FastifyRequest, _reply: FastifyReply) => {
|
||||||
|
const { username, password, email } = request.body as IRegisterRequest;
|
||||||
|
|
||||||
|
const newUser = await registerUser({
|
||||||
|
username: username,
|
||||||
|
password: password,
|
||||||
|
email: email,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!newUser) {
|
||||||
|
return {
|
||||||
|
error: "user already exists",
|
||||||
|
} as IRegisterResponseError;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: newUser.id,
|
||||||
|
username: newUser.username,
|
||||||
|
registerDate: newUser.registerDate?.getTime(),
|
||||||
|
} as IRegisterResponseSuccess;
|
||||||
|
};
|
||||||
|
|
||||||
|
const postLogin = async (request: FastifyRequest, _reply: FastifyReply) => {
|
||||||
|
const { username, password } = request.body as ILoginRequest;
|
||||||
|
|
||||||
|
const session = await loginUser({
|
||||||
|
username: username,
|
||||||
|
password: password,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!session) {
|
||||||
|
return {
|
||||||
|
ownerId: "",
|
||||||
|
error: "incorrect credentials",
|
||||||
|
} as ILoginResponseError;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: session.id,
|
||||||
|
ownerId: session.userId,
|
||||||
|
token: session.token,
|
||||||
|
} as ILoginResponseSuccess;
|
||||||
|
};
|
||||||
|
|
||||||
|
export { postRegister, postLogin };
|
||||||
3
src/controllers/auth/index.ts
Normal file
3
src/controllers/auth/index.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
export * from "./auth.js";
|
||||||
|
export * from "./routes.js";
|
||||||
|
export * from "./types.js";
|
||||||
9
src/controllers/auth/routes.ts
Normal file
9
src/controllers/auth/routes.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { type FastifyInstance } from "fastify";
|
||||||
|
import * as controller from "./auth.js";
|
||||||
|
|
||||||
|
const authRoutes = async (fastify: FastifyInstance) => {
|
||||||
|
fastify.post(`/register`, controller.postRegister);
|
||||||
|
fastify.post(`/login`, controller.postLogin);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { authRoutes };
|
||||||
40
src/controllers/auth/types.ts
Normal file
40
src/controllers/auth/types.ts
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
interface IRegisterRequest {
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
email?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IRegisterResponseSuccess {
|
||||||
|
id: string;
|
||||||
|
username: string;
|
||||||
|
registerDate: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IRegisterResponseError {
|
||||||
|
error: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ILoginRequest {
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ILoginResponseSuccess {
|
||||||
|
id: string;
|
||||||
|
ownerId: string;
|
||||||
|
token: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ILoginResponseError {
|
||||||
|
ownerId: string;
|
||||||
|
error: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
type IRegisterRequest,
|
||||||
|
type IRegisterResponseSuccess,
|
||||||
|
type IRegisterResponseError,
|
||||||
|
type ILoginRequest,
|
||||||
|
type ILoginResponseSuccess,
|
||||||
|
type ILoginResponseError,
|
||||||
|
};
|
||||||
27
src/controllers/channel/channel.ts
Normal file
27
src/controllers/channel/channel.ts
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
import { type FastifyReply, type FastifyRequest } from "fastify";
|
||||||
|
import type {
|
||||||
|
IChannelParams,
|
||||||
|
IChannelResponseError,
|
||||||
|
IChannelResponseSuccess,
|
||||||
|
} from "./types.js";
|
||||||
|
import { getChannelById } from "../../services/channel/channel.js";
|
||||||
|
|
||||||
|
const getChannel = async (request: FastifyRequest, _reply: FastifyReply) => {
|
||||||
|
const { id } = request.params as IChannelParams;
|
||||||
|
|
||||||
|
const channel = await getChannelById(id);
|
||||||
|
if (!channel) {
|
||||||
|
return {
|
||||||
|
id: id,
|
||||||
|
error: "channel does not exist",
|
||||||
|
} as IChannelResponseError;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: channel.id,
|
||||||
|
name: channel.name,
|
||||||
|
communityId: channel.communityId,
|
||||||
|
} as IChannelResponseSuccess;
|
||||||
|
};
|
||||||
|
|
||||||
|
export { getChannel };
|
||||||
3
src/controllers/channel/index.ts
Normal file
3
src/controllers/channel/index.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
export * from "./channel.js";
|
||||||
|
export * from "./routes.js";
|
||||||
|
export * from "./types.js";
|
||||||
8
src/controllers/channel/routes.ts
Normal file
8
src/controllers/channel/routes.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { type FastifyInstance } from "fastify";
|
||||||
|
import * as controller from "./channel.js";
|
||||||
|
|
||||||
|
const channelRoutes = async (fastify: FastifyInstance) => {
|
||||||
|
fastify.get(`/:id`, controller.getChannel);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { channelRoutes };
|
||||||
20
src/controllers/channel/types.ts
Normal file
20
src/controllers/channel/types.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
interface IChannelParams {
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IChannelResponseError {
|
||||||
|
id: string;
|
||||||
|
error: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IChannelResponseSuccess {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
communityId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
type IChannelParams,
|
||||||
|
type IChannelResponseError,
|
||||||
|
type IChannelResponseSuccess,
|
||||||
|
};
|
||||||
27
src/controllers/community/community.ts
Normal file
27
src/controllers/community/community.ts
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
import { type FastifyReply, type FastifyRequest } from "fastify";
|
||||||
|
import type {
|
||||||
|
ICommunityParams,
|
||||||
|
ICommunityResponseError,
|
||||||
|
ICommunityResponseSuccess,
|
||||||
|
} from "./types.js";
|
||||||
|
import { getCommunityById } from "../../services/community/community.js";
|
||||||
|
|
||||||
|
const getCommunity = async (request: FastifyRequest, _reply: FastifyReply) => {
|
||||||
|
const { id } = request.params as ICommunityParams;
|
||||||
|
|
||||||
|
const community = await getCommunityById(id);
|
||||||
|
if (!community) {
|
||||||
|
return {
|
||||||
|
id: id,
|
||||||
|
error: "community does not exist",
|
||||||
|
} as ICommunityResponseError;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: community.id,
|
||||||
|
name: community.name,
|
||||||
|
description: community.description,
|
||||||
|
} as ICommunityResponseSuccess;
|
||||||
|
};
|
||||||
|
|
||||||
|
export { getCommunity };
|
||||||
3
src/controllers/community/index.ts
Normal file
3
src/controllers/community/index.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
export * from "./community.js";
|
||||||
|
export * from "./routes.js";
|
||||||
|
export * from "./types.js";
|
||||||
8
src/controllers/community/routes.ts
Normal file
8
src/controllers/community/routes.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { type FastifyInstance } from "fastify";
|
||||||
|
import * as controller from "./community.js";
|
||||||
|
|
||||||
|
const communityRoutes = async (fastify: FastifyInstance) => {
|
||||||
|
fastify.get(`/:id`, controller.getCommunity);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { communityRoutes };
|
||||||
20
src/controllers/community/types.ts
Normal file
20
src/controllers/community/types.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
interface ICommunityParams {
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ICommunityResponseError {
|
||||||
|
id: string;
|
||||||
|
error: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ICommunityResponseSuccess {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
type ICommunityParams,
|
||||||
|
type ICommunityResponseError,
|
||||||
|
type ICommunityResponseSuccess,
|
||||||
|
};
|
||||||
3
src/controllers/role/index.ts
Normal file
3
src/controllers/role/index.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
export * from "./role.js";
|
||||||
|
export * from "./routes.js";
|
||||||
|
export * from "./types.js";
|
||||||
27
src/controllers/role/role.ts
Normal file
27
src/controllers/role/role.ts
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
import { type FastifyReply, type FastifyRequest } from "fastify";
|
||||||
|
import type {
|
||||||
|
IRoleParams,
|
||||||
|
IRoleResponseError,
|
||||||
|
IRoleResponseSuccess,
|
||||||
|
} from "./types.js";
|
||||||
|
import { getRoleById } from "../../services/role/role.js";
|
||||||
|
|
||||||
|
const getRole = async (request: FastifyRequest, _reply: FastifyReply) => {
|
||||||
|
const { id } = request.params as IRoleParams;
|
||||||
|
|
||||||
|
const role = await getRoleById(id);
|
||||||
|
if (!role) {
|
||||||
|
return {
|
||||||
|
id: id,
|
||||||
|
error: "role does not exist",
|
||||||
|
} as IRoleResponseError;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: role.id,
|
||||||
|
name: role.name,
|
||||||
|
communityId: role.communityId,
|
||||||
|
} as IRoleResponseSuccess;
|
||||||
|
};
|
||||||
|
|
||||||
|
export { getRole };
|
||||||
8
src/controllers/role/routes.ts
Normal file
8
src/controllers/role/routes.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { type FastifyInstance } from "fastify";
|
||||||
|
import * as controller from "./role.js";
|
||||||
|
|
||||||
|
const roleRoutes = async (fastify: FastifyInstance) => {
|
||||||
|
fastify.get(`/:id`, controller.getRole);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { roleRoutes };
|
||||||
16
src/controllers/role/types.ts
Normal file
16
src/controllers/role/types.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
interface IRoleParams {
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IRoleResponseError {
|
||||||
|
id: string;
|
||||||
|
error: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IRoleResponseSuccess {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
communityId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { type IRoleParams, type IRoleResponseError, type IRoleResponseSuccess };
|
||||||
3
src/controllers/session/index.ts
Normal file
3
src/controllers/session/index.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
export * from "./session.js";
|
||||||
|
export * from "./routes.js";
|
||||||
|
export * from "./types.js";
|
||||||
8
src/controllers/session/routes.ts
Normal file
8
src/controllers/session/routes.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { type FastifyInstance } from "fastify";
|
||||||
|
import * as controller from "./session.js";
|
||||||
|
|
||||||
|
const sessionRoutes = async (fastify: FastifyInstance) => {
|
||||||
|
fastify.get(`/:id`, controller.getSession);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { sessionRoutes };
|
||||||
26
src/controllers/session/session.ts
Normal file
26
src/controllers/session/session.ts
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { type FastifyReply, type FastifyRequest } from "fastify";
|
||||||
|
import type {
|
||||||
|
ISessionParams,
|
||||||
|
ISessionResponseError,
|
||||||
|
ISessionResponseSuccess,
|
||||||
|
} from "./types.js";
|
||||||
|
import { getSessionById } from "../../services/session/session.js";
|
||||||
|
|
||||||
|
const getSession = async (request: FastifyRequest, _reply: FastifyReply) => {
|
||||||
|
const { id } = request.params as ISessionParams;
|
||||||
|
|
||||||
|
const session = await getSessionById(id);
|
||||||
|
if (!session) {
|
||||||
|
return {
|
||||||
|
id: id,
|
||||||
|
error: "session does not exist",
|
||||||
|
} as ISessionResponseError;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: session.id,
|
||||||
|
userId: session.userId,
|
||||||
|
} as ISessionResponseSuccess;
|
||||||
|
};
|
||||||
|
|
||||||
|
export { getSession };
|
||||||
19
src/controllers/session/types.ts
Normal file
19
src/controllers/session/types.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
interface ISessionParams {
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ISessionResponseError {
|
||||||
|
id: string;
|
||||||
|
error: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ISessionResponseSuccess {
|
||||||
|
id: string;
|
||||||
|
userId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
type ISessionParams,
|
||||||
|
type ISessionResponseError,
|
||||||
|
type ISessionResponseSuccess,
|
||||||
|
};
|
||||||
|
|
@ -2,7 +2,8 @@ import { type FastifyInstance } from "fastify";
|
||||||
import * as controller from "./test.js";
|
import * as controller from "./test.js";
|
||||||
|
|
||||||
const testRoutes = async (fastify: FastifyInstance) => {
|
const testRoutes = async (fastify: FastifyInstance) => {
|
||||||
fastify.get("/test", controller.test);
|
fastify.get("/ping", controller.getPing);
|
||||||
|
fastify.get("/test", controller.getTest);
|
||||||
};
|
};
|
||||||
|
|
||||||
export { testRoutes };
|
export { testRoutes };
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
import { type FastifyReply, type FastifyRequest } from "fastify";
|
import { type FastifyReply, type FastifyRequest } from "fastify";
|
||||||
import { testdb } from "../../store/store.js";
|
|
||||||
|
|
||||||
const test = async (request: FastifyRequest, reply: FastifyReply) => {
|
const getPing = async (_request: FastifyRequest, _reply: FastifyReply) => {
|
||||||
testdb();
|
return [{ message: "pong" }];
|
||||||
|
|
||||||
return [{ name: "Alice" }];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export { test };
|
const getTest = async (_request: FastifyRequest, _reply: FastifyReply) => {
|
||||||
|
return [{ message: "ok" }];
|
||||||
|
};
|
||||||
|
|
||||||
|
export { getPing, getTest };
|
||||||
|
|
|
||||||
3
src/controllers/user/index.ts
Normal file
3
src/controllers/user/index.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
export * from "./user.js";
|
||||||
|
export * from "./routes.js";
|
||||||
|
export * from "./types.js";
|
||||||
9
src/controllers/user/routes.ts
Normal file
9
src/controllers/user/routes.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { type FastifyInstance } from "fastify";
|
||||||
|
import * as controller from "./user.js";
|
||||||
|
|
||||||
|
const userRoutes = async (fastify: FastifyInstance) => {
|
||||||
|
fastify.get(`/:id`, controller.getUser);
|
||||||
|
fastify.get(`/:id/sessions`, controller.getSessions);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { userRoutes };
|
||||||
41
src/controllers/user/types.ts
Normal file
41
src/controllers/user/types.ts
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
interface IUserParams {
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IUserResponseError {
|
||||||
|
id: string;
|
||||||
|
error: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IUserResponseSuccess {
|
||||||
|
id: string;
|
||||||
|
username: string;
|
||||||
|
email: string;
|
||||||
|
description: string;
|
||||||
|
admin: boolean;
|
||||||
|
registerDate: number;
|
||||||
|
lastLogin: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ISessionsResponseError {
|
||||||
|
id: string;
|
||||||
|
error: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ISessionsResponseSuccess {
|
||||||
|
sessions: ISessionsResponseSession[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ISessionsResponseSession {
|
||||||
|
id: string;
|
||||||
|
userId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
type IUserParams,
|
||||||
|
type IUserResponseError,
|
||||||
|
type IUserResponseSuccess,
|
||||||
|
type ISessionsResponseError,
|
||||||
|
type ISessionsResponseSuccess,
|
||||||
|
type ISessionsResponseSession,
|
||||||
|
};
|
||||||
53
src/controllers/user/user.ts
Normal file
53
src/controllers/user/user.ts
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
import { type FastifyReply, type FastifyRequest } from "fastify";
|
||||||
|
import type {
|
||||||
|
IUserParams,
|
||||||
|
IUserResponseError,
|
||||||
|
IUserResponseSuccess,
|
||||||
|
ISessionsResponseError,
|
||||||
|
ISessionsResponseSuccess,
|
||||||
|
} from "./types.js";
|
||||||
|
import { getUserById, getUserSessionsById } from "../../services/user/user.js";
|
||||||
|
|
||||||
|
const getUser = async (request: FastifyRequest, _reply: FastifyReply) => {
|
||||||
|
const { id } = request.params as IUserParams;
|
||||||
|
|
||||||
|
const user = await getUserById(id);
|
||||||
|
if (!user) {
|
||||||
|
return {
|
||||||
|
id: id,
|
||||||
|
error: "user does not exist",
|
||||||
|
} as IUserResponseError;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: user.id,
|
||||||
|
username: user.username,
|
||||||
|
email: user.email,
|
||||||
|
description: user.description,
|
||||||
|
admin: user.admin,
|
||||||
|
registerDate: user.registerDate.getTime(),
|
||||||
|
lastLogin: user.lastLogin?.getTime() ?? 0,
|
||||||
|
} as IUserResponseSuccess;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSessions = async (request: FastifyRequest, _reply: FastifyReply) => {
|
||||||
|
const { id } = request.params as IUserParams;
|
||||||
|
const authHeader = request.headers["authorization"];
|
||||||
|
|
||||||
|
const sessions = await getUserSessionsById(id, authHeader);
|
||||||
|
if (!sessions) {
|
||||||
|
return {
|
||||||
|
id: id,
|
||||||
|
error: "user does not exist or you have no access",
|
||||||
|
} as ISessionsResponseError;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
sessions: sessions.map((session) => ({
|
||||||
|
id: session.id,
|
||||||
|
userId: session.userId,
|
||||||
|
})),
|
||||||
|
} as ISessionsResponseSuccess;
|
||||||
|
};
|
||||||
|
|
||||||
|
export { getUser, getSessions };
|
||||||
59
src/helpers.ts
Normal file
59
src/helpers.ts
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
import jwt from "jsonwebtoken";
|
||||||
|
import type { Session, User } from "./generated/prisma/client.js";
|
||||||
|
import { getDB } from "./store/store.js";
|
||||||
|
|
||||||
|
const getJwtSecret = () => {
|
||||||
|
return process.env.JWT_SECRET || "";
|
||||||
|
};
|
||||||
|
|
||||||
|
const verifyToken = (token: string): string | jwt.JwtPayload | null => {
|
||||||
|
try {
|
||||||
|
return jwt.verify(token, getJwtSecret());
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSessionFromToken = async (token: string): Promise<Session | null> => {
|
||||||
|
return await getDB().session.findFirst({
|
||||||
|
where: {
|
||||||
|
token: token,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const getUserFromToken = async (token: string): Promise<User | null> => {
|
||||||
|
const session = await getSessionFromToken(token);
|
||||||
|
|
||||||
|
return await getDB().user.findFirst({
|
||||||
|
where: {
|
||||||
|
id: session?.userId ?? "invalid",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const getUserFromAuth = async (
|
||||||
|
authHeader: string | undefined,
|
||||||
|
): Promise<User | null> => {
|
||||||
|
const token = authHeader?.replace("Bearer ", "");
|
||||||
|
|
||||||
|
const verified = verifyToken(token ?? "") !== null;
|
||||||
|
if (!verified || !token) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await getUserFromToken(token);
|
||||||
|
if (!user) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return user;
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
getJwtSecret,
|
||||||
|
verifyToken,
|
||||||
|
getSessionFromToken,
|
||||||
|
getUserFromToken,
|
||||||
|
getUserFromAuth,
|
||||||
|
};
|
||||||
12
src/index.ts
12
src/index.ts
|
|
@ -2,12 +2,24 @@ import { config } from "./config.js";
|
||||||
|
|
||||||
import Fastify from "fastify";
|
import Fastify from "fastify";
|
||||||
import { testRoutes } from "./controllers/test/routes.js";
|
import { testRoutes } from "./controllers/test/routes.js";
|
||||||
|
import { authRoutes } from "./controllers/auth/routes.js";
|
||||||
|
import { userRoutes } from "./controllers/user/routes.js";
|
||||||
|
import { sessionRoutes } from "./controllers/session/routes.js";
|
||||||
|
import { communityRoutes } from "./controllers/community/routes.js";
|
||||||
|
import { channelRoutes } from "./controllers/channel/routes.js";
|
||||||
|
import { roleRoutes } from "./controllers/role/routes.js";
|
||||||
|
|
||||||
const app = Fastify({
|
const app = Fastify({
|
||||||
logger: true,
|
logger: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
app.register(testRoutes);
|
app.register(testRoutes);
|
||||||
|
app.register(authRoutes, { prefix: "/api/v1/auth" });
|
||||||
|
app.register(userRoutes, { prefix: "/api/v1/user" });
|
||||||
|
app.register(sessionRoutes, { prefix: "/api/v1/session" });
|
||||||
|
app.register(communityRoutes, { prefix: "/api/v1/community" });
|
||||||
|
app.register(channelRoutes, { prefix: "/api/v1/channel" });
|
||||||
|
app.register(roleRoutes, { prefix: "/api/v1/role" });
|
||||||
|
|
||||||
app.listen({ port: config.port }, (err, address) => {
|
app.listen({ port: config.port }, (err, address) => {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
|
|
|
||||||
72
src/services/auth/auth.ts
Normal file
72
src/services/auth/auth.ts
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
import argon2 from "argon2";
|
||||||
|
import jwt from "jsonwebtoken";
|
||||||
|
|
||||||
|
import type { User, Session } from "../../generated/prisma/client.js";
|
||||||
|
import { getDB } from "../../store/store.js";
|
||||||
|
import type { IUserLogin, IUserRegistration } from "./types.js";
|
||||||
|
import { getJwtSecret } from "../../helpers.js";
|
||||||
|
|
||||||
|
const registerUser = async (
|
||||||
|
registration: IUserRegistration,
|
||||||
|
): Promise<User | null> => {
|
||||||
|
const existingUser = await getDB().user.findUnique({
|
||||||
|
where: { username: registration.username },
|
||||||
|
});
|
||||||
|
if (existingUser) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const passwordHash = await hashPassword(registration.password);
|
||||||
|
|
||||||
|
let newUser: User | null = null;
|
||||||
|
try {
|
||||||
|
newUser = await getDB().user.create({
|
||||||
|
data: {
|
||||||
|
username: registration.username,
|
||||||
|
passwordHash: passwordHash,
|
||||||
|
email: registration.email ?? null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return newUser;
|
||||||
|
};
|
||||||
|
|
||||||
|
const loginUser = async (login: IUserLogin): Promise<Session | null> => {
|
||||||
|
const user = await getDB().user.findUnique({
|
||||||
|
where: { username: login.username },
|
||||||
|
});
|
||||||
|
|
||||||
|
const passwordCorrect = await argon2.verify(
|
||||||
|
user?.passwordHash ?? "",
|
||||||
|
login.password,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!user || !passwordCorrect) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await getDB().session.create({
|
||||||
|
data: {
|
||||||
|
token: createToken(user.id),
|
||||||
|
userId: user.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const hashPassword = async (password: string): Promise<string> => {
|
||||||
|
return await argon2.hash(password, {
|
||||||
|
type: argon2.argon2id,
|
||||||
|
memoryCost: 2 ** 16,
|
||||||
|
timeCost: 4,
|
||||||
|
parallelism: 1,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const createToken = (userId: string) => {
|
||||||
|
return jwt.sign({ sub: userId }, getJwtSecret());
|
||||||
|
};
|
||||||
|
|
||||||
|
export { registerUser, loginUser, hashPassword };
|
||||||
2
src/services/auth/index.ts
Normal file
2
src/services/auth/index.ts
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
export * from "./auth.js";
|
||||||
|
export * from "./types.js";
|
||||||
12
src/services/auth/types.ts
Normal file
12
src/services/auth/types.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
interface IUserRegistration {
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
email?: string | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IUserLogin {
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { type IUserRegistration, type IUserLogin };
|
||||||
10
src/services/channel/channel.ts
Normal file
10
src/services/channel/channel.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import type { Channel } from "../../generated/prisma/client.js";
|
||||||
|
import { getDB } from "../../store/store.js";
|
||||||
|
|
||||||
|
const getChannelById = async (id: string): Promise<Channel | null> => {
|
||||||
|
return await getDB().channel.findUnique({
|
||||||
|
where: { id: id },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export { getChannelById };
|
||||||
1
src/services/channel/index.ts
Normal file
1
src/services/channel/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export * from "./channel.js";
|
||||||
10
src/services/community/community.ts
Normal file
10
src/services/community/community.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import type { Community } from "../../generated/prisma/client.js";
|
||||||
|
import { getDB } from "../../store/store.js";
|
||||||
|
|
||||||
|
const getCommunityById = async (id: string): Promise<Community | null> => {
|
||||||
|
return await getDB().community.findUnique({
|
||||||
|
where: { id: id },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export { getCommunityById };
|
||||||
1
src/services/community/index.ts
Normal file
1
src/services/community/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export * from "./community.js";
|
||||||
1
src/services/role/index.ts
Normal file
1
src/services/role/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export * from "./role.js";
|
||||||
10
src/services/role/role.ts
Normal file
10
src/services/role/role.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import type { Role } from "../../generated/prisma/client.js";
|
||||||
|
import { getDB } from "../../store/store.js";
|
||||||
|
|
||||||
|
const getRoleById = async (id: string): Promise<Role | null> => {
|
||||||
|
return await getDB().role.findUnique({
|
||||||
|
where: { id: id },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export { getRoleById };
|
||||||
1
src/services/session/index.ts
Normal file
1
src/services/session/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export * from "./session.js";
|
||||||
10
src/services/session/session.ts
Normal file
10
src/services/session/session.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import type { Session } from "../../generated/prisma/client.js";
|
||||||
|
import { getDB } from "../../store/store.js";
|
||||||
|
|
||||||
|
const getSessionById = async (id: string): Promise<Session | null> => {
|
||||||
|
return await getDB().session.findUnique({
|
||||||
|
where: { id: id },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export { getSessionById };
|
||||||
1
src/services/user/index.ts
Normal file
1
src/services/user/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export * from "./user.js";
|
||||||
27
src/services/user/user.ts
Normal file
27
src/services/user/user.ts
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
import type { User, Session } from "../../generated/prisma/client.js";
|
||||||
|
import { getUserFromAuth } from "../../helpers.js";
|
||||||
|
import { getDB } from "../../store/store.js";
|
||||||
|
|
||||||
|
const getUserById = async (id: string): Promise<User | null> => {
|
||||||
|
return await getDB().user.findUnique({
|
||||||
|
where: { id: id },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const getUserSessionsById = async (
|
||||||
|
id: string,
|
||||||
|
authHeader: string | undefined,
|
||||||
|
): Promise<Session[] | null> => {
|
||||||
|
const user = await getUserFromAuth(authHeader);
|
||||||
|
if (!user || user.id !== id) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await getDB().session.findMany({
|
||||||
|
where: {
|
||||||
|
userId: id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export { getUserById, getUserSessionsById };
|
||||||
|
|
@ -1,2 +1 @@
|
||||||
export * from "./store.js";
|
export * from "./store.js";
|
||||||
export * from "./types.js";
|
|
||||||
|
|
|
||||||
|
|
@ -10,18 +10,8 @@ const adapter = new PrismaPg(pool);
|
||||||
|
|
||||||
const prisma = new PrismaClient({ adapter });
|
const prisma = new PrismaClient({ adapter });
|
||||||
|
|
||||||
async function testdb() {
|
const getDB = (): PrismaClient => {
|
||||||
const test = await prisma.user.findMany();
|
return prisma;
|
||||||
/*
|
};
|
||||||
const user = await prisma.user.create({
|
|
||||||
data: { name: "Alice", email: `alice${Math.random()}@example.com` },
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log("Created user:", user);
|
export { getDB };
|
||||||
|
|
||||||
const test = await prisma.user.findMany();
|
|
||||||
console.log(test);
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
export { testdb };
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
interface IState {}
|
|
||||||
|
|
||||||
export { type IState };
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue