Remove redis and add healthcheck endpoint
This commit is contained in:
@@ -31,6 +31,19 @@ app.use(express.json())
|
|||||||
app.set('view engine', 'ejs')
|
app.set('view engine', 'ejs')
|
||||||
app.use(express.static('views/public'))
|
app.use(express.static('views/public'))
|
||||||
|
|
||||||
|
// Health check endpoint
|
||||||
|
app.get(['/health', '/healthz', '/api/health'], (req, res) => {
|
||||||
|
const mongoose = require('mongoose')
|
||||||
|
const isDbConnected = mongoose.connection.readyState === 1
|
||||||
|
res.status(isDbConnected ? 200 : 503).json({
|
||||||
|
status: isDbConnected ? 'healthy' : 'unhealthy',
|
||||||
|
uptime: process.uptime(),
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
service: 'belonging-manager-api',
|
||||||
|
database: isDbConnected ? 'connected' : 'disconnected',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
app.use('/api', routerEndpoints)
|
app.use('/api', routerEndpoints)
|
||||||
|
|
||||||
const { registerEventHandlers } = require('./events/index')
|
const { registerEventHandlers } = require('./events/index')
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
const mongoose = require('mongoose')
|
||||||
|
|
||||||
|
const loginAttemptSchema = new mongoose.Schema(
|
||||||
|
{
|
||||||
|
key: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
unique: true,
|
||||||
|
index: true,
|
||||||
|
},
|
||||||
|
attempts: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
expiresAt: {
|
||||||
|
type: Date,
|
||||||
|
required: true,
|
||||||
|
index: { expires: 0 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ timestamps: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
const LoginAttempt = mongoose.model('LoginAttempt', loginAttemptSchema)
|
||||||
|
|
||||||
|
module.exports = LoginAttempt
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
// userService.js
|
// userService.js
|
||||||
const User = require('./userModel')
|
const User = require('./userModel')
|
||||||
const AppError = require('../../globalErrorHandler/AppError')
|
const AppError = require('../../globalErrorHandler/AppError')
|
||||||
const redisClient = require('../../setup/redis')
|
const LoginAttempt = require('./loginAttemptModel')
|
||||||
const jwt = require('jsonwebtoken')
|
const jwt = require('jsonwebtoken')
|
||||||
const bcrypt = require('bcryptjs')
|
const bcrypt = require('bcryptjs')
|
||||||
const maxAge = 60 * 60 * 24 * 90
|
const maxAge = 60 * 60 * 24 * 90
|
||||||
@@ -44,39 +44,40 @@ const signup = async (name, email, password, passwordConfirm) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const loginAttempt = async (phrase) => {
|
const loginAttempt = async (phrase) => {
|
||||||
const attemptExist = await redisClient.exists(phrase + '#attempts')
|
const key = phrase + '#attempts'
|
||||||
|
const limit = parseInt(process.env.LOGIN_ATTEMPT_LIMIT) || 10
|
||||||
|
const ttlSeconds = 3600
|
||||||
|
const now = new Date()
|
||||||
|
|
||||||
if (attemptExist === 0) {
|
let attemptRecord = await LoginAttempt.findOne({ key })
|
||||||
await redisClient.set(
|
|
||||||
phrase + '#attempts',
|
if (attemptRecord && attemptRecord.expiresAt <= now) {
|
||||||
0,
|
await LoginAttempt.deleteOne({ _id: attemptRecord._id })
|
||||||
{
|
attemptRecord = null
|
||||||
EX: 3600,
|
}
|
||||||
},
|
|
||||||
null
|
if (!attemptRecord) {
|
||||||
)
|
const expiresAt = new Date(Date.now() + ttlSeconds * 1000)
|
||||||
|
await LoginAttempt.create({
|
||||||
|
key,
|
||||||
|
attempts: 0,
|
||||||
|
expiresAt,
|
||||||
|
})
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// if user phrase(email or phone) exist return error with expire time left for login
|
// if user phrase(email or phone) exist return error with expire time left for login
|
||||||
if (parseInt(await redisClient.get(phrase + '#attempts')) >= parseInt(process.env.LOGIN_ATTEMPT_LIMIT)) {
|
if (attemptRecord.attempts >= limit) {
|
||||||
const limitTime = await redisClient.ttl(phrase + '#attempts')
|
const limitTime = Math.max(0, Math.ceil((attemptRecord.expiresAt.getTime() - Date.now()) / 1000))
|
||||||
const minutes = (limitTime / 60).toFixed(0)
|
const minutes = (limitTime / 60).toFixed(0)
|
||||||
throw new AppError('MaximumLoginReached', 500, {
|
throw new AppError('MaximumLoginReached', 500, {
|
||||||
minutes,
|
minutes,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attemptExist === 1) {
|
attemptRecord.attempts += 1
|
||||||
const attempts = parseInt(await redisClient.get(phrase + '#attempts'))
|
attemptRecord.expiresAt = new Date(Date.now() + ttlSeconds * 1000)
|
||||||
await redisClient.set(
|
await attemptRecord.save()
|
||||||
phrase + '#attempts',
|
|
||||||
attempts + 1 + '',
|
|
||||||
{
|
|
||||||
EX: 3600,
|
|
||||||
},
|
|
||||||
null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const loginUser = async (email, password) => {
|
const loginUser = async (email, password) => {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ MONGODB_URL=mongodb://admin:*135792468*@localhost:27018/marbm?authSource=admin
|
|||||||
NODE_ENV=development
|
NODE_ENV=development
|
||||||
PORT=3000
|
PORT=3000
|
||||||
JWT_SECRET=Y@RJ$WT!@K0NS@MPL!
|
JWT_SECRET=Y@RJ$WT!@K0NS@MPL!
|
||||||
REDIS_URL=redis://:135792468@localhost:6380/0
|
|
||||||
LOGIN_ATTEMPT_LIMIT=900
|
LOGIN_ATTEMPT_LIMIT=900
|
||||||
SUPER_ADMIN_ROLE_ID=66a2da5b9b989a8909948848
|
SUPER_ADMIN_ROLE_ID=66a2da5b9b989a8909948848
|
||||||
TYPE_SENSE_URL=localhost:8109
|
TYPE_SENSE_URL=localhost:8109
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ MONGODB_URL=mongodb://admin:*135792468*@localhost:27018/marbm?authSource=admin
|
|||||||
NODE_ENV=development
|
NODE_ENV=development
|
||||||
PORT=3000
|
PORT=3000
|
||||||
JWT_SECRET=Y@RJNS@MPL!
|
JWT_SECRET=Y@RJNS@MPL!
|
||||||
REDIS_URL= redis://:135792468@localhost:6379/0
|
|
||||||
LOGIN_ATTEMPT_LIMIT=10
|
LOGIN_ATTEMPT_LIMIT=10
|
||||||
SUPER_ADMIN_ROLE_ID=66a2da5b9b989a8909948848
|
SUPER_ADMIN_ROLE_ID=66a2da5b9b989a8909948848
|
||||||
TYPE_SENSE_URL=localhost:8108
|
TYPE_SENSE_URL=localhost:8108
|
||||||
|
|||||||
Generated
-91
@@ -23,7 +23,6 @@
|
|||||||
"mongoose": "^8.9.0",
|
"mongoose": "^8.9.0",
|
||||||
"mongoose-sequence": "^6.0.1",
|
"mongoose-sequence": "^6.0.1",
|
||||||
"multer": "^1.4.5-lts.1",
|
"multer": "^1.4.5-lts.1",
|
||||||
"redis": "^4.7.0",
|
|
||||||
"typesense": "^1.8.2",
|
"typesense": "^1.8.2",
|
||||||
"validator": "^13.12.0"
|
"validator": "^13.12.0"
|
||||||
},
|
},
|
||||||
@@ -51,59 +50,6 @@
|
|||||||
"sparse-bitfield": "^3.0.3"
|
"sparse-bitfield": "^3.0.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@redis/bloom": {
|
|
||||||
"version": "1.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz",
|
|
||||||
"integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==",
|
|
||||||
"peerDependencies": {
|
|
||||||
"@redis/client": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@redis/client": {
|
|
||||||
"version": "1.6.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.0.tgz",
|
|
||||||
"integrity": "sha512-aR0uffYI700OEEH4gYnitAnv3vzVGXCFvYfdpu/CJKvk4pHfLPEy/JSZyrpQ+15WhXe1yJRXLtfQ84s4mEXnPg==",
|
|
||||||
"dependencies": {
|
|
||||||
"cluster-key-slot": "1.1.2",
|
|
||||||
"generic-pool": "3.9.0",
|
|
||||||
"yallist": "4.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@redis/graph": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.1.tgz",
|
|
||||||
"integrity": "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==",
|
|
||||||
"peerDependencies": {
|
|
||||||
"@redis/client": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@redis/json": {
|
|
||||||
"version": "1.0.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.7.tgz",
|
|
||||||
"integrity": "sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ==",
|
|
||||||
"peerDependencies": {
|
|
||||||
"@redis/client": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@redis/search": {
|
|
||||||
"version": "1.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@redis/search/-/search-1.2.0.tgz",
|
|
||||||
"integrity": "sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw==",
|
|
||||||
"peerDependencies": {
|
|
||||||
"@redis/client": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@redis/time-series": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g==",
|
|
||||||
"peerDependencies": {
|
|
||||||
"@redis/client": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@types/webidl-conversions": {
|
"node_modules/@types/webidl-conversions": {
|
||||||
"version": "7.0.3",
|
"version": "7.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz",
|
||||||
@@ -442,14 +388,6 @@
|
|||||||
"fsevents": "~2.3.2"
|
"fsevents": "~2.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"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/color-convert": {
|
"node_modules/color-convert": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
@@ -949,14 +887,6 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/generic-pool": {
|
|
||||||
"version": "3.9.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz",
|
|
||||||
"integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/get-intrinsic": {
|
"node_modules/get-intrinsic": {
|
||||||
"version": "1.2.6",
|
"version": "1.2.6",
|
||||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.6.tgz",
|
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.6.tgz",
|
||||||
@@ -1887,22 +1817,6 @@
|
|||||||
"node": ">=8.10.0"
|
"node": ">=8.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/redis": {
|
|
||||||
"version": "4.7.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/redis/-/redis-4.7.0.tgz",
|
|
||||||
"integrity": "sha512-zvmkHEAdGMn+hMRXuMBtu4Vo5P6rHQjLoHftu+lBqq8ZTA3RCVC/WzD790bkKKiNFp7d5/9PcSD19fJyyRvOdQ==",
|
|
||||||
"workspaces": [
|
|
||||||
"./packages/*"
|
|
||||||
],
|
|
||||||
"dependencies": {
|
|
||||||
"@redis/bloom": "1.2.0",
|
|
||||||
"@redis/client": "1.6.0",
|
|
||||||
"@redis/graph": "1.1.1",
|
|
||||||
"@redis/json": "1.0.7",
|
|
||||||
"@redis/search": "1.2.0",
|
|
||||||
"@redis/time-series": "1.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/regenerator-runtime": {
|
"node_modules/regenerator-runtime": {
|
||||||
"version": "0.14.1",
|
"version": "0.14.1",
|
||||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
|
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
|
||||||
@@ -2411,11 +2325,6 @@
|
|||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.4"
|
"node": ">=0.4"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"node_modules/yallist": {
|
|
||||||
"version": "4.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
|
||||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,6 @@
|
|||||||
"mongoose": "^8.9.0",
|
"mongoose": "^8.9.0",
|
||||||
"mongoose-sequence": "^6.0.1",
|
"mongoose-sequence": "^6.0.1",
|
||||||
"multer": "^1.4.5-lts.1",
|
"multer": "^1.4.5-lts.1",
|
||||||
"redis": "^4.7.0",
|
|
||||||
"typesense": "^1.8.2",
|
"typesense": "^1.8.2",
|
||||||
"validator": "^13.12.0"
|
"validator": "^13.12.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
const mongoose = require('mongoose')
|
const mongoose = require('mongoose')
|
||||||
const env = process.env.NODE_ENV || 'development'
|
const env = process.env.NODE_ENV || 'development'
|
||||||
const redisClient = require('../setup/redis')
|
|
||||||
const seederRunner = require('./seederRunner')
|
const seederRunner = require('./seederRunner')
|
||||||
const chalk = require('chalk')
|
const chalk = require('chalk')
|
||||||
|
|
||||||
@@ -12,8 +11,6 @@ module.exports = (app) => {
|
|||||||
.connect(connectionString, {})
|
.connect(connectionString, {})
|
||||||
.then(async function () {
|
.then(async function () {
|
||||||
console.log('MongoDB Database Connected')
|
console.log('MongoDB Database Connected')
|
||||||
await redisClient.connect().catch((err) => console.log('Redis connect warning:', err.message))
|
|
||||||
console.log('Redis connection Established')
|
|
||||||
|
|
||||||
console.log(chalk.red.bold('-------- Running Seeders ---------'))
|
console.log(chalk.red.bold('-------- Running Seeders ---------'))
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
const mongoose = require('mongoose')
|
const mongoose = require('mongoose')
|
||||||
const redisClient = require('./redis')
|
|
||||||
const port = process.env.PORT || 3000
|
const port = process.env.PORT || 3000
|
||||||
const env = process.env.NODE_ENV || 'development'
|
const env = process.env.NODE_ENV || 'development'
|
||||||
module.exports = (app) => {
|
module.exports = (app) => {
|
||||||
@@ -20,12 +19,8 @@ module.exports = (app) => {
|
|||||||
// Create a document to force MongoDB to actually create the DB and collection
|
// Create a document to force MongoDB to actually create the DB and collection
|
||||||
await MyModel.create({ name: 'test' })
|
await MyModel.create({ name: 'test' })
|
||||||
|
|
||||||
redisClient.connect().then((r) => {
|
app.listen(port, function () {
|
||||||
console.log('Redis connection Established')
|
console.log('Server Started On Port ' + port)
|
||||||
|
|
||||||
app.listen(port, function () {
|
|
||||||
console.log('Server Started On Port ' + port)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.catch(function (err) {
|
.catch(function (err) {
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
const redis = require('redis')
|
|
||||||
const env = process.env.NODE_ENV || 'development'
|
|
||||||
|
|
||||||
let connectionString
|
|
||||||
if (env === 'development') {
|
|
||||||
connectionString = process.env.REDIS_URL
|
|
||||||
} else if (env === 'test') {
|
|
||||||
connectionString = process.env.TEST_REDIS_URL
|
|
||||||
} else if (env === 'production') {
|
|
||||||
connectionString = process.env.REDIS_URL
|
|
||||||
}
|
|
||||||
const client = redis.createClient({ url: connectionString })
|
|
||||||
|
|
||||||
client.on('error', function (err) {
|
|
||||||
console.log(err)
|
|
||||||
})
|
|
||||||
|
|
||||||
module.exports = client
|
|
||||||
Reference in New Issue
Block a user