Skip to content

Commit d0c8df7

Browse files
committed
Added support for secure IP extraction from proxies, removed API rate limiter, added Cache-Control header, added option to change update interval
1 parent 7befbac commit d0c8df7

File tree

8 files changed

+94
-30
lines changed

8 files changed

+94
-30
lines changed

.env-example

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,26 @@
1+
# Server Configuration
12
SERVER_HOST="0.0.0.0"
23
SERVER_PORT=3000
34

5+
# Trading212 API Credentials
6+
# Get these from your Trading212 account dashboard
7+
TRADING212_API_KEY=your_trading212_api_key_here
8+
TRADING212_API_SECRET=your_trading212_api_secret_here
9+
10+
# Trading212 API Update Interval (in milliseconds)
11+
# Minimum: 5000 (5 seconds) due to Trading212 API limits
12+
# Default: 10000
13+
UPDATE_INTERVAL=10000
14+
15+
# Logging Configuration
16+
# 0 = ERROR, 1 = WARN, 2 = AUDIT, 3 = INFO, 4 = HTTP, 5 = DEBUG, 6 = VERBOSE, 7 = SILLY
17+
# Default: 3 (INFO) - Recommended: 3 for production, 5 for development
418
LOGGER_LEVEL=3
519

6-
TRADING212_API_KEY=your_trading212_api_key
7-
TRADING212_API_SECRET=your_trading212_api_secret
20+
# Proxy Configuration
21+
# Important: Set this to match your deployment environment to prevent IP spoofing
22+
# Options: "aws" (AWS ELB/ALB), "azure" (Azure), "cloudflare" (Cloudflare),
23+
# "gcp" (Google Cloud), "nginx" (Nginx), "vercel" (Vercel),
24+
# "direct" (no proxy/development), "development" (dev with proxy headers)
25+
# Default: "direct"
26+
PROXY=direct

README.md

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,32 @@ A high-performance stock API built with Bun that fetches real-time stock data fr
2424
Create a `.env` file in your project root:
2525

2626
```toml
27-
TRADING212_API_KEY=your_api_key_here
28-
TRADING212_API_SECRET=your_api_secret_here
27+
# Server Configuration
28+
SERVER_HOST="0.0.0.0"
29+
SERVER_PORT=3000
30+
31+
# Trading212 API Credentials
32+
# Get these from your Trading212 account dashboard
33+
TRADING212_API_KEY=your_trading212_api_key_here
34+
TRADING212_API_SECRET=your_trading212_api_secret_here
35+
36+
# Trading212 API Update Interval (in milliseconds)
37+
# Minimum: 5000 (5 seconds) due to Trading212 API limits
38+
# Default: 10000
39+
UPDATE_INTERVAL=10000
40+
41+
# Logging Configuration
42+
# 0 = ERROR, 1 = WARN, 2 = AUDIT, 3 = INFO, 4 = HTTP, 5 = DEBUG, 6 = VERBOSE, 7 = SILLY
43+
# Default: 3 (INFO) - Recommended: 3 for production, 5 for development
2944
LOGGER_LEVEL=3
45+
46+
# Proxy Configuration
47+
# Important: Set this to match your deployment environment to prevent IP spoofing
48+
# Options: "aws" (AWS ELB/ALB), "azure" (Azure), "cloudflare" (Cloudflare),
49+
# "gcp" (Google Cloud), "nginx" (Nginx), "vercel" (Vercel),
50+
# "direct" (no proxy/development), "development" (dev with proxy headers)
51+
# Default: "direct"
52+
PROXY=direct
3053
```
3154

3255
### Running with Docker Compose
@@ -40,9 +63,11 @@ services:
4063
ports:
4164
- "3000:3000"
4265
environment:
43-
- LOGGER_LEVEL
4466
- TRADING212_API_KEY
4567
- TRADING212_API_SECRET
68+
- UPDATE_INTERVAL
69+
- LOGGER_LEVEL
70+
- PROXY
4671
healthcheck:
4772
test: ["CMD", "curl", "-f", "http://localhost:3000"]
4873
interval: 10s
@@ -65,7 +90,9 @@ docker run -d \
6590
-p 3000:3000 \
6691
-e TRADING212_API_KEY=your_key \
6792
-e TRADING212_API_SECRET=your_secret \
68-
-e LOGGER_LEVEL=3 \
93+
-e UPDATE_INTERVAL=10000 \
94+
-e LOGGER_LEVEL=3 \
95+
-e PROXY=direct \
6996
rabbitcompany/rabbitstockapi:latest
7097
```
7198

@@ -80,13 +107,14 @@ Health Check
80107
"message": "RabbitStockAPI is running",
81108
"stocksCount": 16,
82109
"instrumentsCount": 12811,
110+
"updateInterval": "10000ms",
83111
"lastUpdate": "2025-10-31T13:49:16.007Z"
84112
}
85113
```
86114

87-
### GET `/stocks`
115+
### GET `/prices`
88116

89-
Stock Data
117+
Stock prices data
90118

91119
```json
92120
{

docker-compose.override.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ services:
77
ports:
88
- "3000:3000"
99
environment:
10-
- LOGGER_LEVEL
1110
- TRADING212_API_KEY
1211
- TRADING212_API_SECRET
12+
- UPDATE_INTERVAL
13+
- LOGGER_LEVEL
14+
- PROXY
1315
healthcheck:
1416
test: ["CMD", "curl", "-f", "http://localhost:3000"]
1517
interval: 10s

docker-compose.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ services:
66
ports:
77
- "3000:3000"
88
environment:
9-
- LOGGER_LEVEL
109
- TRADING212_API_KEY
1110
- TRADING212_API_SECRET
11+
- UPDATE_INTERVAL
12+
- LOGGER_LEVEL
13+
- PROXY
1214
healthcheck:
1315
test: ["CMD", "curl", "-f", "http://localhost:3000"]
1416
interval: 10s

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "rabbitstockapi",
33
"module": "src/index.ts",
4-
"version": "1.0.1",
4+
"version": "2.0.0",
55
"type": "module",
66
"private": true,
77
"scripts": {

src/index.ts

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import { Web } from "@rabbit-company/web";
55
import { Logger } from "./logger";
66
import { cors } from "@rabbit-company/web-middleware/cors";
77
import { logger } from "@rabbit-company/web-middleware/logger";
8-
import { Algorithm, rateLimit } from "@rabbit-company/web-middleware/rate-limit";
8+
import { IP_EXTRACTION_PRESETS, ipExtract } from "@rabbit-company/web-middleware/ip-extract";
9+
import type { CloudProvider } from "./types";
910

1011
const { apiKey, apiSecret } = validateEnvironment();
1112
const config = {
@@ -16,6 +17,13 @@ const config = {
1617

1718
const host = process.env.SERVER_HOST || "0.0.0.0";
1819
const port = parseInt(process.env.SERVER_PORT || "3000") || 3000;
20+
const proxy = Object.keys(IP_EXTRACTION_PRESETS).includes(process.env.PROXY || "direct") ? (process.env.PROXY as CloudProvider) : "direct";
21+
const updateInterval = parseInt(process.env.UPDATE_INTERVAL || "10000") || 10000;
22+
const actualInterval = Math.max(updateInterval, 5000);
23+
24+
if (updateInterval < 5000) {
25+
Logger.warn(`Update interval too low: ${updateInterval}ms. Using minimum 5000ms for Trading212 API compliance`);
26+
}
1927

2028
const tradingClient = new Trading212Client(config);
2129
const stockCache = new StockCache();
@@ -35,25 +43,26 @@ app.use(
3543
})
3644
);
3745

38-
app.use(
39-
rateLimit({
40-
algorithm: Algorithm.FIXED_WINDOW,
41-
windowMs: 10 * 1000, // 10 seconds
42-
max: 10,
43-
})
44-
);
46+
app.use(ipExtract(proxy));
4547

4648
app.get("/", (c) => {
47-
return c.json({
48-
message: "RabbitStockAPI is running",
49-
stocksCount: stockCache.getStockCount(),
50-
instrumentsCount: stockCache.getInstrumentCount(),
51-
lastUpdate: new Date().toISOString(),
52-
});
49+
return c.json(
50+
{
51+
message: "RabbitStockAPI is running",
52+
stocksCount: stockCache.getStockCount(),
53+
instrumentsCount: stockCache.getInstrumentCount(),
54+
updateInterval: `${actualInterval}ms`,
55+
lastUpdate: new Date().toISOString(),
56+
},
57+
200,
58+
{ "Cache-Control": `public, s-maxage=${Math.floor(actualInterval / 2 / 1000)}, max-age=0, stale-while-revalidate=300, stale-if-error=86400` }
59+
);
5360
});
5461

55-
app.get("/stocks", (c) => {
56-
return c.json(stockCache.getStocks());
62+
app.get("/prices", (c) => {
63+
return c.json(stockCache.getStocks(), 200, {
64+
"Cache-Control": `public, s-maxage=${Math.floor(actualInterval / 2 / 1000)}, max-age=0, stale-while-revalidate=300, stale-if-error=86400`,
65+
});
5766
});
5867

5968
async function fetchInstruments(): Promise<void> {
@@ -84,10 +93,12 @@ async function initializeApp(): Promise<void> {
8493
await fetchInstruments();
8594
await updateStockPrices();
8695

87-
setInterval(updateStockPrices, 10000);
96+
const actualInterval = Math.max(updateInterval, 5000);
97+
setInterval(updateStockPrices, actualInterval);
8898

8999
Logger.info("RabbitStockAPI started successfully");
90-
Logger.info(`Server running on http://localhost:${port}`);
100+
Logger.info(`Server running on http://${host}:${port}`);
101+
Logger.info(`Stock updates every ${actualInterval}ms (Trading212 compliant)`);
91102
Logger.info("Available endpoints:");
92103
Logger.info(" GET / - Health check");
93104
Logger.info(" GET /stocks - Stock data");

src/logger.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Logger as RabbitLogger } from "@rabbit-company/web-middleware/logger";
22

33
export const Logger = new RabbitLogger({
4-
level: parseInt(process.env.LOGGER_LEVEL || "") || 3,
4+
level: parseInt(process.env.LOGGER_LEVEL || "3") || 3,
55
});

src/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
export type CloudProvider = "aws" | "azure" | "cloudflare" | "development" | "direct" | "gcp" | "nginx" | "vercel";
2+
13
export interface Instrument {
24
ticker: string;
35
type: string;

0 commit comments

Comments
 (0)