Skip to content

Commit dcf8800

Browse files
committed
Improvements:
- Max Attachment Size - Rate Limiting
1 parent 0260636 commit dcf8800

13 files changed

+67
-19
lines changed

.env.example

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ PORT=3000 # OPTIONAL
33
API_KEY=your_global_api_key_here # OPTIONAL
44
BASE_WEBHOOK_URL=http://localhost:3000/localCallbackExample # MANDATORY
55
ENABLE_LOCAL_CALLBACK_EXAMPLE=TRUE # OPTIONAL, NOT RECOMMENDED FOR PRODUCTION
6+
MAX_ATTACHMENT_SIZE=10000000 # IN BYTES
67

78
# Session File Storage
89
SESSIONS_PATH=./sessions # OPTIONAL

.github/workflows/dependabot.yml

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
version: 2
2+
updates:
3+
- package-ecosystem: "npm" # See documentation for possible values
4+
directory: "/" # Location of package manifests
5+
schedule:
6+
interval: "weekly"

assets/basic_start.gif

275 KB
Loading

docker-compose.yml

+1
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,6 @@ services:
1111
# - API_KEY=your_global_api_key_here # OPTIONAL
1212
- BASE_WEBHOOK_URL=http://localhost:3000/localCallbackExample
1313
- ENABLE_LOCAL_CALLBACK_EXAMPLE=TRUE # OPTIONAL, NOT RECOMMENDED FOR PRODUCTION
14+
- MAX_ATTACHMENT_SIZE=5000000 # IN BYTES
1415
volumes:
1516
- ./sessions:/usr/src/app/sessions # Mount the local ./sessions/ folder to the container's /usr/src/app/sessions folder

package-lock.json

+18
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"axios": "^1.3.5",
1212
"dotenv": "^16.0.3",
1313
"express": "^4.18.2",
14+
"express-rate-limit": "^6.7.0",
1415
"qrcode-terminal": "^0.12.0",
1516
"whatsapp-web.js": "^1.19.5"
1617
},

server.js

+9-8
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1-
const app = require("./src/app");
2-
require("dotenv").config();
1+
const app = require('./src/app')
2+
const { baseWebhookURL } = require('./src/config')
3+
require('dotenv').config()
34

45
// Start the server
5-
const port = process.env.PORT || 3000;
6+
const port = process.env.PORT || 3000
67

78
// Check if BASE_WEBHOOK_URL environment variable is available
8-
if (!process.env.BASE_WEBHOOK_URL) {
9-
console.error("BASE_WEBHOOK_URL environment variable is not available. Exiting...");
10-
process.exit(1); // Terminate the application with an error code
9+
if (!baseWebhookURL) {
10+
console.error('BASE_WEBHOOK_URL environment variable is not available. Exiting...')
11+
process.exit(1) // Terminate the application with an error code
1112
}
1213

1314
app.listen(port, () => {
14-
console.log(`Server running on port ${port}`);
15-
});
15+
console.log(`Server running on port ${port}`)
16+
})

src/app.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
require('./routes')
22
const { restoreSessions } = require('./sessions')
33
const { routes } = require('./routes')
4-
5-
// Import required modules
64
const app = require('express')()
75
const bodyParser = require('body-parser')
6+
const { maxAttachmentSize } = require('./config')
87

9-
// Initialize Express app
8+
// Initialize Express app (file limit is 100mb from WhatsApp)
109
app.disable('x-powered-by')
11-
app.use(bodyParser.json({ limit: '100mb' }))
10+
app.use(bodyParser.json({ limit: maxAttachmentSize }))
11+
app.use(bodyParser.urlencoded({ limit: maxAttachmentSize, extended: true }))
1212
app.use('/', routes)
1313

1414
restoreSessions()

src/config.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ const sessionFolderPath = process.env.SESSIONS_PATH || './sessions'
66
const enableLocalCallbackExample = process.env.ENABLE_LOCAL_CALLBACK_EXAMPLE === 'TRUE'
77
const globalApiKey = process.env.API_KEY
88
const baseWebhookURL = process.env.BASE_WEBHOOK_URL
9+
const maxAttachmentSize = process.env.MAX_ATTACHMENT_SIZE || 10000000
910

1011
module.exports = {
1112
sessionFolderPath,
1213
enableLocalCallbackExample,
1314
globalApiKey,
14-
baseWebhookURL
15+
baseWebhookURL,
16+
maxAttachmentSize
1517
}

src/middleware.js

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
const { globalApiKey } = require('./config')
22
const { sendErrorResponse } = require('./utils')
33
const { validateSession } = require('./sessions')
4+
const rateLimiter = require('express-rate-limit')
45

56
// Middleware for securing endpoints with API key
67
const apikeyMiddleware = (req, res, next) => {
@@ -21,7 +22,14 @@ const sessionValidationMiddleware = async (req, res, next) => {
2122
next()
2223
}
2324

25+
const rateLimiterMiddleware = rateLimiter({
26+
max: 100,
27+
windowMS: 1000, // 10 seconds
28+
message: "You can't make any more requests at the moment. Try again later"
29+
})
30+
2431
module.exports = {
2532
sessionValidationMiddleware,
26-
apikeyMiddleware
33+
apikeyMiddleware,
34+
rateLimiterMiddleware
2735
}

src/routes.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ const { MessageMedia } = require('whatsapp-web.js')
22
const fs = require('fs')
33
const routes = require('express').Router()
44
const qrcode = require('qrcode-terminal')
5-
const { apikeyMiddleware, sessionValidationMiddleware } = require('./middleware')
5+
const { apikeyMiddleware, sessionValidationMiddleware, rateLimiterMiddleware } = require('./middleware')
66
const { sessionFolderPath, enableLocalCallbackExample } = require('./config')
77
const { sessions, setupSession, deleteSession, validateSession } = require('./sessions')
88
const { sendErrorResponse } = require('./utils')
@@ -18,7 +18,7 @@ routes.get('/ping', (req, res) => {
1818

1919
// API basic callback
2020
if (enableLocalCallbackExample) {
21-
routes.post('/localCallbackExample', apikeyMiddleware, (req, res) => {
21+
routes.post('/localCallbackExample', [apikeyMiddleware, rateLimiterMiddleware], (req, res) => {
2222
try {
2323
const { sessionId, dataType, data } = req.body
2424
if (dataType === 'qr') { qrcode.generate(data.qr, { small: true }) }

src/sessions.js

+10-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const { Client, LocalAuth } = require('whatsapp-web.js')
22
const fs = require('fs')
33
const sessions = new Map()
4-
const { sessionFolderPath } = require('./config')
4+
const { sessionFolderPath, maxAttachmentSize } = require('./config')
55
const { triggerWebhook } = require('./utils')
66

77
// Function to validate if the session is ready
@@ -111,8 +111,12 @@ async function setupSession (sessionId) {
111111
client.on('message', async (message) => {
112112
triggerWebhook(sessionId, 'message', { message })
113113
if (message.hasMedia) {
114-
const media = await message.downloadMedia()
115-
triggerWebhook(sessionId, 'media', { media })
114+
if (message._data && message._data.size && message._data.size < maxAttachmentSize) {
115+
const media = await message.downloadMedia()
116+
triggerWebhook(sessionId, 'media', { media })
117+
} else {
118+
console.log('Attachment too large')
119+
}
116120
}
117121
})
118122

@@ -153,6 +157,9 @@ async function setupSession (sessionId) {
153157

154158
// Function to check if folder is writeable
155159
const deleteSessionFolder = (sessionId) => {
160+
if (!/^[a-zA-Z0-9]+$/.test(sessionId)) {
161+
throw new Error('Invalid sessionId')
162+
}
156163
fs.rmSync(`${sessionFolderPath}/session-${sessionId}`, { recursive: true, force: true }, async err => {
157164
console.log(err)
158165
await new Promise(resolve => setTimeout(resolve, 200))

tests/api.test.js

+3
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ describe('API health checks', () => {
2929
.send({ sessionId: '1', dataType: 'testDataType', data: 'testData' })
3030
expect(response.status).toBe(200)
3131
expect(response.body).toEqual({ success: true })
32+
33+
await new Promise(resolve => setTimeout(resolve, 1000))
34+
3235
expect(fs.existsSync('./sessions_test/message_log.txt')).toBe(true)
3336
expect(fs.readFileSync('./sessions_test/message_log.txt', 'utf-8')).toEqual('(1) testDataType: "testData"\r\n')
3437
})

0 commit comments

Comments
 (0)