mirror of
https://github.com/MrUnknownDE/utools.git
synced 2026-04-19 14:13:44 +02:00
feat: Add initial backend server with various utility APIs, Sentry, logging, rate limiting, and a multi-arch Docker build workflow.
This commit is contained in:
2
.github/workflows/docker-build-push.yml
vendored
2
.github/workflows/docker-build-push.yml
vendored
@@ -17,6 +17,8 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
REGISTRY: docker.io
|
REGISTRY: docker.io
|
||||||
DOCKERHUB_USER_LC: ${{ secrets.DOCKERHUB_USERNAME }}
|
DOCKERHUB_USER_LC: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
|
|||||||
40
backend/package-lock.json
generated
40
backend/package-lock.json
generated
@@ -18,6 +18,7 @@
|
|||||||
"macaddress": "^0.5.3",
|
"macaddress": "^0.5.3",
|
||||||
"pino": "^9.6.0",
|
"pino": "^9.6.0",
|
||||||
"pino-pretty": "^13.0.0",
|
"pino-pretty": "^13.0.0",
|
||||||
|
"qs": "^6.14.1",
|
||||||
"whois-json": "^2.0.4"
|
"whois-json": "^2.0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -855,6 +856,21 @@
|
|||||||
"npm": "1.2.8000 || >= 1.4.16"
|
"npm": "1.2.8000 || >= 1.4.16"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/body-parser/node_modules/qs": {
|
||||||
|
"version": "6.13.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
|
||||||
|
"integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"side-channel": "^1.0.6"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/bytes": {
|
"node_modules/bytes": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||||
@@ -1224,6 +1240,21 @@
|
|||||||
"express": "^4.11 || 5 || ^5.0.0-beta.1"
|
"express": "^4.11 || 5 || ^5.0.0-beta.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/express/node_modules/qs": {
|
||||||
|
"version": "6.13.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
|
||||||
|
"integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"side-channel": "^1.0.6"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/fast-copy": {
|
"node_modules/fast-copy": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.2.tgz",
|
||||||
@@ -1950,11 +1981,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/qs": {
|
"node_modules/qs": {
|
||||||
"version": "6.13.0",
|
"version": "6.14.1",
|
||||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
|
||||||
"integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
|
"integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"side-channel": "^1.0.6"
|
"side-channel": "^1.1.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.6"
|
"node": ">=0.6"
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
"macaddress": "^0.5.3",
|
"macaddress": "^0.5.3",
|
||||||
"pino": "^9.6.0",
|
"pino": "^9.6.0",
|
||||||
"pino-pretty": "^13.0.0",
|
"pino-pretty": "^13.0.0",
|
||||||
|
"qs": "^6.14.1",
|
||||||
"whois-json": "^2.0.4"
|
"whois-json": "^2.0.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,7 @@ const whois = require('whois-json');
|
|||||||
const pino = require('pino');
|
const pino = require('pino');
|
||||||
|
|
||||||
// Import utilities
|
// Import utilities
|
||||||
const { isValidIp, isValidDomain } = require('../utils');
|
const { isValidIp, isValidDomain, isPrivateIp } = require('../utils');
|
||||||
|
|
||||||
// Logger for this module
|
// Logger for this module
|
||||||
const logger = pino({ level: process.env.LOG_LEVEL || 'info' });
|
const logger = pino({ level: process.env.LOG_LEVEL || 'info' });
|
||||||
@@ -26,6 +26,11 @@ router.get('/', async (req, res, next) => {
|
|||||||
return res.status(400).json({ success: false, error: 'Invalid domain name or IP address provided for WHOIS lookup.' });
|
return res.status(400).json({ success: false, error: 'Invalid domain name or IP address provided for WHOIS lookup.' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isValidIp(query) && isPrivateIp(query)) {
|
||||||
|
logger.warn({ requestIp, query }, 'Attempt to WHOIS lookup private IP blocked');
|
||||||
|
return res.status(403).json({ success: false, error: 'WHOIS lookup for private or local IP addresses is not supported.' });
|
||||||
|
}
|
||||||
|
|
||||||
// Note: No isPrivateIp check here, as WHOIS for IPs might be desired regardless of range,
|
// Note: No isPrivateIp check here, as WHOIS for IPs might be desired regardless of range,
|
||||||
// and domain lookups don't involve IP ranges.
|
// and domain lookups don't involve IP ranges.
|
||||||
|
|
||||||
|
|||||||
@@ -71,11 +71,11 @@ app.set('trust proxy', parseInt(process.env.TRUST_PROXY_COUNT || '2', 10)); // A
|
|||||||
// --- Rate Limiter ---
|
// --- Rate Limiter ---
|
||||||
// Apply a general limiter to most routes
|
// Apply a general limiter to most routes
|
||||||
const generalLimiter = rateLimit({
|
const generalLimiter = rateLimit({
|
||||||
windowMs: 5 * 60 * 1000, // 5 minutes
|
windowMs: parseInt(process.env.RATE_LIMIT_WINDOW_MS || (5 * 60 * 1000).toString(), 10), // Default 5 minutes
|
||||||
max: parseInt(process.env.RATE_LIMIT_MAX || (process.env.NODE_ENV === 'production' ? '20' : '200'), 10), // Requests per window per IP, ensure integer
|
max: parseInt(process.env.RATE_LIMIT_MAX || (process.env.NODE_ENV === 'production' ? '20' : '200'), 10), // Requests per window per IP
|
||||||
standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
|
standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
|
||||||
legacyHeaders: false, // Disable the `X-RateLimit-*` headers
|
legacyHeaders: false, // Disable the `X-RateLimit-*` headers
|
||||||
message: { success: false, error: 'Too many requests from this IP, please try again after 5 minutes' },
|
message: { success: false, error: 'Too many requests from this IP, please try again after a while' },
|
||||||
keyGenerator: (req, res) => req.ip, // Use client IP address from Express
|
keyGenerator: (req, res) => req.ip, // Use client IP address from Express
|
||||||
handler: (req, res, next, options) => {
|
handler: (req, res, next, options) => {
|
||||||
logger.warn({ ip: req.ip, route: req.originalUrl }, 'Rate limit exceeded');
|
logger.warn({ ip: req.ip, route: req.originalUrl }, 'Rate limit exceeded');
|
||||||
@@ -87,15 +87,8 @@ const generalLimiter = rateLimit({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Apply the limiter to specific API routes that perform external actions
|
// Apply the limiter to ALL API routes
|
||||||
// Note: /api/ipinfo and /api/version are often excluded as they are less resource-intensive
|
app.use('/api', generalLimiter);
|
||||||
app.use('/api/ping', generalLimiter);
|
|
||||||
app.use('/api/traceroute', generalLimiter);
|
|
||||||
app.use('/api/lookup', generalLimiter);
|
|
||||||
app.use('/api/dns-lookup', generalLimiter);
|
|
||||||
app.use('/api/whois-lookup', generalLimiter);
|
|
||||||
app.use('/api/port-scan', generalLimiter);
|
|
||||||
app.use('/api/mac-lookup', generalLimiter);
|
|
||||||
|
|
||||||
|
|
||||||
// --- API Routes ---
|
// --- API Routes ---
|
||||||
|
|||||||
Reference in New Issue
Block a user