mirror of
https://github.com/hknsh/project-knedita.git
synced 2024-11-28 17:41:15 +00:00
Implemented rate limit, added Redis database, added more verifications
This commit is contained in:
parent
be8dd3482a
commit
f743521fae
9 changed files with 241 additions and 10 deletions
|
@ -8,7 +8,12 @@ DB_HOST=postgres
|
|||
# Use this instead if you want to use locally
|
||||
# DB_HOST=localhost
|
||||
|
||||
DATABASE_URL=postgresql://<placeholder>:<placeholder>@${DB_HOST}:5432/${POSTGRES_DB}?schema=
|
||||
DATABASE_URL=postgresql://<placeholder>:<placeholder>@${DB_HOST}:5432/${POSTGRES_DB}?schema=<placeholder>
|
||||
|
||||
# Redis
|
||||
REDIS_HOST=redis
|
||||
REDIS_PORT=6379
|
||||
REDIS_PASSWORD=not_a_production_pass # Please change this
|
||||
|
||||
# Express
|
||||
SERVER_PORT=<placeholder>
|
||||
|
|
|
@ -7,7 +7,7 @@ An open-source social media.
|
|||
|
||||
**Client**: React, TailwindCSS and Radix UI (Primitives and Icons)
|
||||
|
||||
**Server**: ExpressJS, Jest, Docker, Postgresql, Prisma, Localstack, SWC and Typescript
|
||||
**Server**: ExpressJS, Jest, Docker, Postgresql, Redis, Prisma, Localstack, SWC and Typescript
|
||||
|
||||
## To-do - Backend
|
||||
|
||||
|
@ -21,9 +21,9 @@ An open-source social media.
|
|||
- Like posts
|
||||
- Probably pinned posts
|
||||
- Authentication ✅
|
||||
- Add more verification (like, if the password is too short)
|
||||
- Add more verification (like, if the password is too short) ✅
|
||||
- Set display name ✅
|
||||
- Add rate limit
|
||||
- Add rate limit ✅
|
||||
|
||||
## Known problems
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ services:
|
|||
- 8080:8080
|
||||
depends_on:
|
||||
- postgres
|
||||
- redis
|
||||
env_file:
|
||||
- .env
|
||||
|
||||
|
@ -25,6 +26,18 @@ services:
|
|||
volumes:
|
||||
- postgres:/var/lib/postgresql/data
|
||||
|
||||
redis:
|
||||
image: redis:alpine
|
||||
restart: unless-stopped
|
||||
container_name: redis
|
||||
ports:
|
||||
- 6379:6379
|
||||
command: redis-server --save 20 1 --loglevel warning --requirepass not_a_production_pass
|
||||
volumes:
|
||||
- redis:/data
|
||||
|
||||
volumes:
|
||||
postgres:
|
||||
name: backend-db
|
||||
redis:
|
||||
driver: local
|
174
package-lock.json
generated
174
package-lock.json
generated
|
@ -14,7 +14,10 @@
|
|||
"compression": "^1.7.4",
|
||||
"dotenv": "^16.3.1",
|
||||
"express": "^4.18.2",
|
||||
"express-rate-limit": "^6.7.1",
|
||||
"ioredis": "^5.3.2",
|
||||
"jsonwebtoken": "^9.0.0",
|
||||
"rate-limit-redis": "^3.0.2",
|
||||
"validator": "^13.9.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -710,6 +713,11 @@
|
|||
"dev": true,
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@ioredis/commands": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz",
|
||||
"integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg=="
|
||||
},
|
||||
"node_modules/@istanbuljs/load-nyc-config": {
|
||||
"version": "1.1.0",
|
||||
"dev": true,
|
||||
|
@ -3084,6 +3092,14 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/cluster-key-slot": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz",
|
||||
"integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/co": {
|
||||
"version": "4.6.0",
|
||||
"dev": true,
|
||||
|
@ -3405,6 +3421,14 @@
|
|||
"version": "1.0.0",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/denque": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
|
||||
"integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==",
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/depd": {
|
||||
"version": "2.0.0",
|
||||
"license": "MIT",
|
||||
|
@ -4378,6 +4402,17 @@
|
|||
"node": ">= 0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/express-rate-limit": {
|
||||
"version": "6.7.1",
|
||||
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-6.7.1.tgz",
|
||||
"integrity": "sha512-eH4VgI64Nowd2vC5Xylx0lLYovWIp2gRFtTklWDbhSDydGAPQUjvr1B7aQ2/ZADrAi6bJ51qSizKIXWAZ1WCQw==",
|
||||
"engines": {
|
||||
"node": ">= 14.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"express": "^4 || ^5"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/debug": {
|
||||
"version": "2.6.9",
|
||||
"license": "MIT",
|
||||
|
@ -5321,6 +5356,29 @@
|
|||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/ioredis": {
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.3.2.tgz",
|
||||
"integrity": "sha512-1DKMMzlIHM02eBBVOFQ1+AolGjs6+xEcM4PDL7NqOS6szq7H9jSaEkIUH6/a5Hl241LzW6JLSiAbNvTQjUupUA==",
|
||||
"dependencies": {
|
||||
"@ioredis/commands": "^1.1.1",
|
||||
"cluster-key-slot": "^1.1.0",
|
||||
"debug": "^4.3.4",
|
||||
"denque": "^2.1.0",
|
||||
"lodash.defaults": "^4.2.0",
|
||||
"lodash.isarguments": "^3.1.0",
|
||||
"redis-errors": "^1.2.0",
|
||||
"redis-parser": "^3.0.0",
|
||||
"standard-as-callback": "^2.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.22.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/ioredis"
|
||||
}
|
||||
},
|
||||
"node_modules/ip": {
|
||||
"version": "1.1.8",
|
||||
"dev": true,
|
||||
|
@ -6424,6 +6482,16 @@
|
|||
"version": "4.17.21",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.defaults": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
|
||||
"integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ=="
|
||||
},
|
||||
"node_modules/lodash.isarguments": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
|
||||
"integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg=="
|
||||
},
|
||||
"node_modules/lodash.memoize": {
|
||||
"version": "4.1.2",
|
||||
"dev": true,
|
||||
|
@ -7792,6 +7860,17 @@
|
|||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/rate-limit-redis": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rate-limit-redis/-/rate-limit-redis-3.0.2.tgz",
|
||||
"integrity": "sha512-4SBK6AzIr9PKkCF4HmSDcJH2O2KKMF3fZEcsbNMXyaL5I9d6X71uOreUldFRiyrRyP+qkQrTxzJ38ZKKN+sScw==",
|
||||
"engines": {
|
||||
"node": ">= 14.5.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"express-rate-limit": "^6"
|
||||
}
|
||||
},
|
||||
"node_modules/raw-body": {
|
||||
"version": "2.5.1",
|
||||
"license": "MIT",
|
||||
|
@ -7866,6 +7945,25 @@
|
|||
"node": ">=8.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/redis-errors": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz",
|
||||
"integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/redis-parser": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz",
|
||||
"integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==",
|
||||
"dependencies": {
|
||||
"redis-errors": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/regexp.prototype.flags": {
|
||||
"version": "1.5.0",
|
||||
"dev": true,
|
||||
|
@ -8360,6 +8458,11 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/standard-as-callback": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz",
|
||||
"integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A=="
|
||||
},
|
||||
"node_modules/standard-engine": {
|
||||
"version": "15.1.0",
|
||||
"dev": true,
|
||||
|
@ -10070,6 +10173,11 @@
|
|||
"version": "1.2.1",
|
||||
"dev": true
|
||||
},
|
||||
"@ioredis/commands": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz",
|
||||
"integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg=="
|
||||
},
|
||||
"@istanbuljs/load-nyc-config": {
|
||||
"version": "1.1.0",
|
||||
"dev": true,
|
||||
|
@ -11650,6 +11758,11 @@
|
|||
"mimic-response": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"cluster-key-slot": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz",
|
||||
"integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA=="
|
||||
},
|
||||
"co": {
|
||||
"version": "4.6.0",
|
||||
"dev": true
|
||||
|
@ -11851,6 +11964,11 @@
|
|||
"delegates": {
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"denque": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
|
||||
"integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw=="
|
||||
},
|
||||
"depd": {
|
||||
"version": "2.0.0"
|
||||
},
|
||||
|
@ -12486,6 +12604,12 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"express-rate-limit": {
|
||||
"version": "6.7.1",
|
||||
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-6.7.1.tgz",
|
||||
"integrity": "sha512-eH4VgI64Nowd2vC5Xylx0lLYovWIp2gRFtTklWDbhSDydGAPQUjvr1B7aQ2/ZADrAi6bJ51qSizKIXWAZ1WCQw==",
|
||||
"requires": {}
|
||||
},
|
||||
"ext-list": {
|
||||
"version": "2.2.2",
|
||||
"dev": true,
|
||||
|
@ -13054,6 +13178,22 @@
|
|||
"side-channel": "^1.0.4"
|
||||
}
|
||||
},
|
||||
"ioredis": {
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.3.2.tgz",
|
||||
"integrity": "sha512-1DKMMzlIHM02eBBVOFQ1+AolGjs6+xEcM4PDL7NqOS6szq7H9jSaEkIUH6/a5Hl241LzW6JLSiAbNvTQjUupUA==",
|
||||
"requires": {
|
||||
"@ioredis/commands": "^1.1.1",
|
||||
"cluster-key-slot": "^1.1.0",
|
||||
"debug": "^4.3.4",
|
||||
"denque": "^2.1.0",
|
||||
"lodash.defaults": "^4.2.0",
|
||||
"lodash.isarguments": "^3.1.0",
|
||||
"redis-errors": "^1.2.0",
|
||||
"redis-parser": "^3.0.0",
|
||||
"standard-as-callback": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"ip": {
|
||||
"version": "1.1.8",
|
||||
"dev": true
|
||||
|
@ -13776,6 +13916,16 @@
|
|||
"lodash": {
|
||||
"version": "4.17.21"
|
||||
},
|
||||
"lodash.defaults": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
|
||||
"integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ=="
|
||||
},
|
||||
"lodash.isarguments": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
|
||||
"integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg=="
|
||||
},
|
||||
"lodash.memoize": {
|
||||
"version": "4.1.2",
|
||||
"dev": true
|
||||
|
@ -14597,6 +14747,12 @@
|
|||
"range-parser": {
|
||||
"version": "1.2.1"
|
||||
},
|
||||
"rate-limit-redis": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rate-limit-redis/-/rate-limit-redis-3.0.2.tgz",
|
||||
"integrity": "sha512-4SBK6AzIr9PKkCF4HmSDcJH2O2KKMF3fZEcsbNMXyaL5I9d6X71uOreUldFRiyrRyP+qkQrTxzJ38ZKKN+sScw==",
|
||||
"requires": {}
|
||||
},
|
||||
"raw-body": {
|
||||
"version": "2.5.1",
|
||||
"requires": {
|
||||
|
@ -14644,6 +14800,19 @@
|
|||
"picomatch": "^2.2.1"
|
||||
}
|
||||
},
|
||||
"redis-errors": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz",
|
||||
"integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w=="
|
||||
},
|
||||
"redis-parser": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz",
|
||||
"integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==",
|
||||
"requires": {
|
||||
"redis-errors": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"regexp.prototype.flags": {
|
||||
"version": "1.5.0",
|
||||
"dev": true,
|
||||
|
@ -14951,6 +15120,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"standard-as-callback": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz",
|
||||
"integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A=="
|
||||
},
|
||||
"standard-engine": {
|
||||
"version": "15.1.0",
|
||||
"dev": true,
|
||||
|
|
|
@ -58,7 +58,10 @@
|
|||
"compression": "^1.7.4",
|
||||
"dotenv": "^16.3.1",
|
||||
"express": "^4.18.2",
|
||||
"express-rate-limit": "^6.7.1",
|
||||
"ioredis": "^5.3.2",
|
||||
"jsonwebtoken": "^9.0.0",
|
||||
"rate-limit-redis": "^3.0.2",
|
||||
"validator": "^13.9.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,13 +11,14 @@ import userUpdateController from './users/user-update'
|
|||
|
||||
// Middlewares
|
||||
import ensureAuthenticated from '../middlewares/ensure-authenticated'
|
||||
import limiter from '../middlewares/rate-limit'
|
||||
|
||||
const usersRouter = Router()
|
||||
|
||||
// Users related
|
||||
usersRouter.post('/auth', userAuthController)
|
||||
usersRouter.post('/delete', ensureAuthenticated, userDeleteController)
|
||||
usersRouter.get('/info', userInfoController)
|
||||
usersRouter.get('/info', limiter, userInfoController)
|
||||
usersRouter.post('/signup', userSignupController)
|
||||
usersRouter.put('/update', ensureAuthenticated, userUpdateController)
|
||||
|
||||
|
|
24
src/middlewares/rate-limit.ts
Normal file
24
src/middlewares/rate-limit.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
import rateLimit from 'express-rate-limit'
|
||||
import RedisStore from 'rate-limit-redis'
|
||||
import RedisClient from 'ioredis'
|
||||
|
||||
const redisPassword = process.env.REDIS_PASSWORD ?? ''
|
||||
const redisHost = process.env.REDIS_HOST ?? ''
|
||||
const redisPort = process.env.REDIS_PORT ?? ''
|
||||
|
||||
const client = new RedisClient(`redis://:${redisPassword}@${redisHost}:${redisPort}/0`)
|
||||
|
||||
const limiter = rateLimit({
|
||||
windowMs: 1 * 60 * 1000, // 60 seconds
|
||||
max: 5,
|
||||
message: { error: 'Too many requests' },
|
||||
legacyHeaders: false,
|
||||
|
||||
// Store configuration
|
||||
store: new RedisStore({
|
||||
// @ts-expect-error - `call` function is not present in @types/ioredis
|
||||
sendCommand: async (...args: string[]) => await client.call(...args)
|
||||
})
|
||||
})
|
||||
|
||||
export default limiter
|
|
@ -17,7 +17,7 @@ async function userAuthService (email: string, password: string): Promise<Object
|
|||
return new Error('Missing fields')
|
||||
}
|
||||
|
||||
const validPassword = await bcrypt.compare(password, user.password)
|
||||
const validPassword = await bcrypt.compare(password.replace(/ /g, ''), user.password)
|
||||
|
||||
if (!validPassword) {
|
||||
return new Error('Invalid email or password')
|
||||
|
|
|
@ -2,13 +2,24 @@ import * as bcrypt from 'bcrypt'
|
|||
import validator from 'validator'
|
||||
import prisma from '../../db'
|
||||
|
||||
const passwordRegex = /^(?=.*[0-9])(?=.*[!@#$%^&*])[a-zA-Z0-9!@#$%^&*]{8,}$/
|
||||
const usernameRegex = /^[a-zA-Z0-9_.]{5,15}$/
|
||||
|
||||
async function userSignupService (username: string, email: string, password: string): Promise<Object | Error> {
|
||||
if (username === undefined || email === undefined || password === undefined) {
|
||||
return new Error('Missing fields')
|
||||
}
|
||||
|
||||
if (!/^[a-zA-Z0-9_.]{5,15}$/.test(username)) {
|
||||
return new Error('Username not allowed. Only alphanumerics characters (uppercase and lowercase words), underscore, dot and it must be between 5 and 15 characters')
|
||||
if (!passwordRegex.test(password)) {
|
||||
return new Error('Password must have 8 characters, one number and one special character.')
|
||||
}
|
||||
|
||||
if (password.trim().length < 8) {
|
||||
return new Error('Password too short')
|
||||
}
|
||||
|
||||
if (!usernameRegex.test(username)) {
|
||||
return new Error('Username not allowed. Only alphanumerics characters (uppercase and lowercase words), underscore, dots and it must be between 5 and 15 characters')
|
||||
}
|
||||
|
||||
if (!validator.isEmail(email)) {
|
||||
|
@ -24,7 +35,7 @@ async function userSignupService (username: string, email: string, password: str
|
|||
}
|
||||
|
||||
const salt = await bcrypt.genSalt(15)
|
||||
const hashedPassword = await bcrypt.hash(password, salt)
|
||||
const hashedPassword = await bcrypt.hash(password.replace(/ /g, ''), salt) // Removes every space in the string
|
||||
|
||||
const user = await prisma.user.create({
|
||||
data: {
|
||||
|
|
Loading…
Reference in a new issue