Skip to content

Commit

Permalink
feat: enable basic rate limiting (#480)
Browse files Browse the repository at this point in the history
* feat: ✨ enable basic rate limiting

* refactor(helper): ♻️ make `asin` regex more specific to allow less false positives

* build(docker): ♻️ add `MAX_REQUESTS` and also prettier

* docs: 📝 add `MAX_REQUESTS` tidbit
  • Loading branch information
djdembeck committed Sep 7, 2022
1 parent 082ec95 commit c6d277b
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 35 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ The stack defaults to 15 replicas for the node-server container. Customize this
Environment variables to add:

- `NODE_ADP_TOKEN`: Aforementioned `ADP_TOKEN` value
- `NODE_MAX_REQUESTS`: Maximum amount of requests per 1 minute period from a single source (default 100)
- `NODE_MONGODB_URI`: MongoDB connection URL, such as `mongodb://mongo/audnexus`
- `NODE_PRIVATE_KEY`: Aforementioned `PRIVATE_KEY` value
- `NODE_REDIS_URL`: Redis connection URL, such as `redis://redis:6379`
Expand Down
43 changes: 22 additions & 21 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
version: "3.7"
version: '3.7'

services:
node-server:
image: ghcr.io/laxamentumtech/audnexus:develop
restart: always
environment:
- ADP_TOKEN=${NODE_ADP_TOKEN}
- MONGODB_URI=${NODE_MONGODB_URI}
- PRIVATE_KEY=${NODE_PRIVATE_KEY}
- REDIS_URL=${NODE_REDIS_URL}
- ADP_TOKEN=${NODE_ADP_TOKEN}
- MAX_REQUESTS=${NODE_MAX_REQUESTS}
- MONGODB_URI=${NODE_MONGODB_URI}
- PRIVATE_KEY=${NODE_PRIVATE_KEY}
- REDIS_URL=${NODE_REDIS_URL}
ports:
- "3000:3000"
- '3000:3000'
depends_on:
- mongo
- redis
Expand All @@ -19,12 +20,12 @@ services:
- internal
deploy:
labels:
- "traefik.enable=true"
- "traefik.docker.network=traefik-overlay"
- "traefik.http.routers.node-server.rule=Host(`${TRAEFIK_DOMAIN}`)"
- "traefik.http.routers.node-server.entrypoints=websecure"
- "traefik.http.routers.redirs.entrypoints=websecure"
- "traefik.http.services.node-server.loadbalancer.server.port=3000"
- 'traefik.enable=true'
- 'traefik.docker.network=traefik-overlay'
- 'traefik.http.routers.node-server.rule=Host(`${TRAEFIK_DOMAIN}`)'
- 'traefik.http.routers.node-server.entrypoints=websecure'
- 'traefik.http.routers.redirs.entrypoints=websecure'
- 'traefik.http.services.node-server.loadbalancer.server.port=3000'
replicas: 15

mongo:
Expand All @@ -45,15 +46,15 @@ services:
image: traefik:v2.8
restart: always
command:
- "--providers.docker=true"
- "--entryPoints.websecure.address=:443"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.websecure.http.tls=true"
- "--certificatesresolvers.myresolver.acme.tlschallenge=true"
- "--certificatesresolvers.myresolver.acme.email=${TRAEFIK_EMAIL}"
- "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
- '--providers.docker=true'
- '--entryPoints.websecure.address=:443'
- '--providers.docker.exposedbydefault=false'
- '--entrypoints.websecure.http.tls=true'
- '--certificatesresolvers.myresolver.acme.tlschallenge=true'
- '--certificatesresolvers.myresolver.acme.email=${TRAEFIK_EMAIL}'
- '--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json'
ports:
- "443:443"
- '443:443'
volumes:
- /mnt/docker/letsencrypt:/letsencrypt
- /var/run/docker.sock:/var/run/docker.sock:ro
Expand All @@ -64,4 +65,4 @@ services:

networks:
traefik-overlay:
internal:
internal:
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"license": "GPL-3.0",
"dependencies": {
"@fastify/cors": "8.1.0",
"@fastify/rate-limit": "^7.4.0",
"@fastify/redis": "6.0.0",
"cheerio": "1.0.0-rc.12",
"domhandler": "5.0.3",
Expand Down
11 changes: 10 additions & 1 deletion pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/helpers/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { PaprDocument } from '#config/typing/papr'
import { ParsedObject } from '#config/typing/unions'

class SharedHelper {
asin10Regex = /(?=.\d)[A-Z\d]{10}/
asin10Regex = /^(B[\dA-Z]{9}|\d{9}(X|\d))$/
asin11Regex = /(?=.\d)[A-Z\d]{11}/
/**
* Creates URL to use in fetchBook
Expand Down
45 changes: 33 additions & 12 deletions src/server.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import cors from '@fastify/cors'
import rateLimit from '@fastify/rate-limit'
import redis from '@fastify/redis'
import { fastify } from 'fastify'

Expand All @@ -20,23 +21,18 @@ const port = Number(process.env.PORT) || 3000
const server = fastify({
logger: {
level: 'warn'
}
},
trustProxy: true
})

// Register routes
server
.register(showBook)
.register(deleteBook)
.register(showChapter)
.register(deleteChapter)
.register(showAuthor)
.register(deleteAuthor)
.register(searchAuthor)

// Register redis if it's present
if (process.env.REDIS_URL) {
console.log('Using Redis')
server.register(redis, { url: process.env.REDIS_URL })
server.register(redis, {
connectTimeout: 500,
maxRetriesPerRequest: 1,
url: process.env.REDIS_URL
})
}

// Setup DB context
Expand All @@ -50,6 +46,31 @@ server.register(cors, {
origin: true
})

// Rate limiting
server.register(rateLimit, {
global: true,
max: Number(process.env.MAX_REQUESTS) || 100,
redis: process.env.REDIS_URL ? server.redis : undefined,
timeWindow: '1 minute'
})
// Send 429 if rate limit is reached
server.setErrorHandler(function (error, _request, reply) {
if (reply.statusCode === 429) {
error.message = 'Rate limit reached. Please try again later.'
}
reply.send(error)
})

// Register routes
server
.register(showBook)
.register(deleteBook)
.register(showChapter)
.register(deleteChapter)
.register(showAuthor)
.register(deleteAuthor)
.register(searchAuthor)

server.listen({ port: port, host: host }, async (err, address) => {
if (err) {
console.error(err)
Expand Down
4 changes: 4 additions & 0 deletions tests/helpers/shared.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ describe('SharedHelper should', () => {
expect(helper.checkAsinValidity('B079LRSMNN')).toBe(true)
expect(helper.checkAsinValidity('12345678910')).toBe(false)
expect(helper.checkAsinValidity('B*79LRSMNN')).toBe(false)
expect(helper.checkAsinValidity('20XORININE')).toBe(false)
expect(helper.checkAsinValidity('1705047572')).toBe(true)
expect(helper.checkAsinValidity('B07Q769RZS')).toBe(true)
expect(helper.checkAsinValidity('B0B9YP4F9P')).toBe(true)
})

test('check data equality', () => {
Expand Down

0 comments on commit c6d277b

Please sign in to comment.