mirror of
https://github.com/jason5ng32/MyIP.git
synced 2026-11-04 08:55:47 +00:00
Improvements
This commit is contained in:
1 parent
ca4db706df
commit
57f03e66ec
10 files changed
+22
-22
No files matched your search
+3
-3
@@ -1,4 +1,4 @@
|
|||||||
# 第一阶段:构建
|
# Build stage
|
||||||
FROM node:24-alpine AS build-stage
|
FROM node:24-alpine AS build-stage
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY package*.json ./
|
COPY package*.json ./
|
||||||
@@ -6,7 +6,7 @@ RUN npm install
|
|||||||
COPY . .
|
COPY . .
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
# 第二阶段:运行
|
# Production stage
|
||||||
FROM node:24-alpine AS production-stage
|
FROM node:24-alpine AS production-stage
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY --from=build-stage /app/node_modules ./node_modules
|
COPY --from=build-stage /app/node_modules ./node_modules
|
||||||
@@ -19,5 +19,5 @@ COPY --from=build-stage /app/common ./common
|
|||||||
|
|
||||||
EXPOSE 18966
|
EXPOSE 18966
|
||||||
|
|
||||||
# 启动应用
|
# Start application
|
||||||
CMD ["npm", "start"]
|
CMD ["npm", "start"]
|
||||||
+13
-13
@@ -49,7 +49,7 @@ const speedLimitSet = parseInt(process.env.SECURITY_DELAY_AFTER || 0, 10);
|
|||||||
|
|
||||||
app.set('trust proxy', 1);
|
app.set('trust proxy', 1);
|
||||||
|
|
||||||
// 获取客户端 IP 的辅助函数。
|
// Helper function to get client IP
|
||||||
function getClientIp(req) {
|
function getClientIp(req) {
|
||||||
const cfIp = req.headers['cf-connecting-ip']; // Cloudflare IP
|
const cfIp = req.headers['cf-connecting-ip']; // Cloudflare IP
|
||||||
const forwardedIps = req.headers['x-forwarded-for'] ? req.headers['x-forwarded-for'].split(',')[0] : null;
|
const forwardedIps = req.headers['x-forwarded-for'] ? req.headers['x-forwarded-for'].split(',')[0] : null;
|
||||||
@@ -57,23 +57,23 @@ function getClientIp(req) {
|
|||||||
return cfIp || forwardedIps || cfIpV6 || req.ip;
|
return cfIp || forwardedIps || cfIpV6 || req.ip;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 将时间戳格式化为限流日志使用的上海时区时间。
|
// Format timestamp for rate limit log using Shanghai time zone
|
||||||
function formatDate(timestamp) {
|
function formatDate(timestamp) {
|
||||||
return new Date(timestamp).toLocaleString('en-US', { timeZone: 'Asia/Shanghai' });
|
return new Date(timestamp).toLocaleString('en-US', { timeZone: 'Asia/Shanghai' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 将触发限流的 IP 写入日志,并累计同一个 IP 被限制的次数。
|
// Write IP that triggered the limit to the log and count the number of times the same IP was limited
|
||||||
function logLimitedIP(ip) {
|
function logLimitedIP(ip) {
|
||||||
const logPath = path.join(__dirname, blackListIPLogFilePath);
|
const logPath = path.join(__dirname, blackListIPLogFilePath);
|
||||||
|
|
||||||
// 如果 logs 目录不存在,则创建
|
// If logs directory does not exist, create it
|
||||||
const logDir = path.dirname(logPath);
|
const logDir = path.dirname(logPath);
|
||||||
if (!fs.existsSync(logDir)) {
|
if (!fs.existsSync(logDir)) {
|
||||||
fs.mkdirSync(logDir, { recursive: true });
|
fs.mkdirSync(logDir, { recursive: true });
|
||||||
console.log('Created log directory:', logDir);
|
console.log('Created log directory:', logDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 读取日志文件,更新 IP 计数,如果文件不存在则创建新的日志文件
|
// Read log file, update IP count, create new log file if it does not exist
|
||||||
fs.readFile(logPath, 'utf8', (err, data) => {
|
fs.readFile(logPath, 'utf8', (err, data) => {
|
||||||
if (err && err.code !== 'ENOENT') {
|
if (err && err.code !== 'ENOENT') {
|
||||||
console.error('Error reading the log file:', err);
|
console.error('Error reading the log file:', err);
|
||||||
@@ -117,7 +117,7 @@ const rateLimiter = rateLimit({
|
|||||||
windowMs: 20 * 60 * 1000,
|
windowMs: 20 * 60 * 1000,
|
||||||
max: rateLimitSet,
|
max: rateLimitSet,
|
||||||
message: 'Too Many Requests',
|
message: 'Too Many Requests',
|
||||||
// 处理超过限流阈值的请求,并按需记录触发限流的 IP。
|
// Handle requests that exceed the rate limit threshold, and record the IP that triggered the limit as needed
|
||||||
handler: (req, res, next) => {
|
handler: (req, res, next) => {
|
||||||
const ip = getClientIp(req);
|
const ip = getClientIp(req);
|
||||||
if (req.rateLimit.current === req.rateLimit.limit + 1 && blackListIPLogFilePath) {
|
if (req.rateLimit.current === req.rateLimit.limit + 1 && blackListIPLogFilePath) {
|
||||||
@@ -130,17 +130,17 @@ const rateLimiter = rateLimit({
|
|||||||
const speedLimiter = slowDown({
|
const speedLimiter = slowDown({
|
||||||
windowMs: 60 * 60 * 1000,
|
windowMs: 60 * 60 * 1000,
|
||||||
delayAfter: speedLimitSet,
|
delayAfter: speedLimitSet,
|
||||||
// 根据命中次数逐步增加响应延迟。
|
// Increase response delay gradually based on the number of hits
|
||||||
delayMs: (hits) => hits * 400,
|
delayMs: (hits) => hits * 400,
|
||||||
})
|
})
|
||||||
|
|
||||||
// 如果 rateLimitSet 为 0,则不启用限流
|
// If rateLimitSet is 0, do not enable rate limiting
|
||||||
if (rateLimitSet !== 0) {
|
if (rateLimitSet !== 0) {
|
||||||
app.use('/api', rateLimiter);
|
app.use('/api', rateLimiter);
|
||||||
console.log('Rate limiter is enabled, limit:', rateLimitSet, 'requests per 60 minutes');
|
console.log('Rate limiter is enabled, limit:', rateLimitSet, 'requests per 60 minutes');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果 deleyAfter 为 0,则不启用延迟
|
// If delayAfter is 0, do not enable delay
|
||||||
if (speedLimitSet !== 0) {
|
if (speedLimitSet !== 0) {
|
||||||
app.use('/api', speedLimiter);
|
app.use('/api', speedLimiter);
|
||||||
console.log('Speed limiter is enabled, slowing down after:', speedLimitSet, 'requests');
|
console.log('Speed limiter is enabled, slowing down after:', speedLimitSet, 'requests');
|
||||||
@@ -170,17 +170,17 @@ app.get('/api/maxmind', requireValidIP(), maxmindHandler);
|
|||||||
app.get('/api/getuserinfo', getUserinfo);
|
app.get('/api/getuserinfo', getUserinfo);
|
||||||
app.put('/api/updateuserachievement', updateUserAchievement);
|
app.put('/api/updateuserachievement', updateUserAchievement);
|
||||||
|
|
||||||
// 使用查询参数处理所有配置请求
|
// Handle all configuration requests using query parameters
|
||||||
app.get('/api/configs', validateConfigs);
|
app.get('/api/configs', validateConfigs);
|
||||||
|
|
||||||
// 设置静态文件服务
|
// Set static file server
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = path.dirname(__filename);
|
const __dirname = path.dirname(__filename);
|
||||||
app.use(express.static(path.join(__dirname, './dist')));
|
app.use(express.static(path.join(__dirname, './dist')));
|
||||||
|
|
||||||
|
|
||||||
// 启动服务器
|
// Start server
|
||||||
app.listen(backEndPort, () => {
|
app.listen(backEndPort, () => {
|
||||||
// 输出监听地址,便于本地运行和进程管理器日志排查。
|
// Output listening address, for local running and process manager log troubleshooting
|
||||||
console.log(`Backend server running on http://localhost:${backEndPort}`);
|
console.log(`Backend server running on http://localhost:${backEndPort}`);
|
||||||
});
|
});
|
||||||
+3
-3
@@ -4,8 +4,8 @@ services:
|
|||||||
myip:
|
myip:
|
||||||
container_name: myip
|
container_name: myip
|
||||||
image: jason5ng32/myip:latest # my-app: x.x.x
|
image: jason5ng32/myip:latest # my-app: x.x.x
|
||||||
stdin_open: true # docker run -i 交互模式
|
stdin_open: true # docker run -i interactive mode
|
||||||
tty: true # docker run -t 终端模式
|
tty: true # docker run -t terminal mode
|
||||||
restart: always # 重启策略 / on-failure:5 重启次数5次 / always 一直重启 / unless-stopped 除非停止 / no 从不重启
|
restart: always # restart policy / on-failure:5 restart 5 times / always restart forever / unless-stopped unless stopped / no never restart
|
||||||
ports:
|
ports:
|
||||||
- "18966:18966"
|
- "18966:18966"
|
||||||
+3
-3
@@ -14,16 +14,16 @@ const frontEndPort = parseInt(process.env.FRONTEND_PORT || 18966, 10);
|
|||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = path.dirname(__filename);
|
const __dirname = path.dirname(__filename);
|
||||||
|
|
||||||
// API 请求代理到后端服务
|
// API Proxy to backend server
|
||||||
frontendApp.use('/api', createProxyMiddleware({
|
frontendApp.use('/api', createProxyMiddleware({
|
||||||
target: `http://localhost:${backEndPort}/api`,
|
target: `http://localhost:${backEndPort}/api`,
|
||||||
changeOrigin: true
|
changeOrigin: true
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// 设置静态文件目录
|
// Set static file directory
|
||||||
frontendApp.use(express.static(path.join(__dirname, './dist')));
|
frontendApp.use(express.static(path.join(__dirname, './dist')));
|
||||||
|
|
||||||
// 启动静态文件服务
|
// Start static file server
|
||||||
frontendApp.listen(frontEndPort, () => {
|
frontendApp.listen(frontEndPort, () => {
|
||||||
console.log(`Static file server running on port http://localhost:${frontEndPort}`);
|
console.log(`Static file server running on port http://localhost:${frontEndPort}`);
|
||||||
});
|
});
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 405 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 435 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 407 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 403 KiB |
Reference in new issue
Block a user