From 403fc62e84a34c4dbc6ff8fe7e23246f47b82416 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Mar 2026 13:05:31 +0000 Subject: [PATCH] fix: address code review feedback - mask phone, add cache limits, cleanup expired entries Co-authored-by: qinfendebingshuo <207279273+qinfendebingshuo@users.noreply.github.com> Agent-Logs-Url: https://github.com/qinfendebingshuo/guanghulab/sessions/38d62fa6-b645-4f42-92dd-00d327337c17 --- .../backend/src/middleware/rateLimiter.ts | 2 +- writing-platform/backend/src/routes/auth.ts | 11 +++++-- .../backend/src/services/aiService.ts | 33 +++++++++++++++++-- .../backend/src/services/smsService.ts | 13 +++++++- 4 files changed, 52 insertions(+), 7 deletions(-) diff --git a/writing-platform/backend/src/middleware/rateLimiter.ts b/writing-platform/backend/src/middleware/rateLimiter.ts index d3044a4c..ae252501 100644 --- a/writing-platform/backend/src/middleware/rateLimiter.ts +++ b/writing-platform/backend/src/middleware/rateLimiter.ts @@ -5,7 +5,7 @@ const requestCounts = new Map(); export function rateLimiter(maxRequests: number = 10, windowMs: number = 60000) { return (req: Request, res: Response, next: NextFunction): void => { - const ip = req.ip || req.socket.remoteAddress || 'unknown'; + const ip = req.ip || req.socket.remoteAddress || req.headers['x-forwarded-for']?.toString() || 'unknown'; const now = Date.now(); const record = requestCounts.get(ip); diff --git a/writing-platform/backend/src/routes/auth.ts b/writing-platform/backend/src/routes/auth.ts index b24bb4f7..c2e2b995 100644 --- a/writing-platform/backend/src/routes/auth.ts +++ b/writing-platform/backend/src/routes/auth.ts @@ -6,6 +6,13 @@ import { rateLimiter } from '../middleware/rateLimiter'; const router = Router(); +function maskPhone(phone: string): string { + if (phone.length >= 7) { + return phone.slice(0, 3) + '****' + phone.slice(-4); + } + return '***'; +} + // Send verification code router.post('/send-code', rateLimiter(5, 60000), async (req: Request, res: Response): Promise => { const { phone } = req.body; @@ -66,7 +73,7 @@ router.post('/register', rateLimiter(5, 60000), async (req: Request, res: Respon id: user.id, nickname: user.nickname, role: user.role, - phone: user.phone, + phone: maskPhone(user.phone), aiCompanion: user.aiCompanion, creditScore: user.creditScore, }, @@ -106,7 +113,7 @@ router.post('/login', rateLimiter(10, 60000), async (req: Request, res: Response id: user.id, nickname: user.nickname, role: user.role, - phone: user.phone, + phone: maskPhone(user.phone), aiCompanion: user.aiCompanion, creditScore: user.creditScore, }, diff --git a/writing-platform/backend/src/services/aiService.ts b/writing-platform/backend/src/services/aiService.ts index 881065a7..0e7e4fe4 100644 --- a/writing-platform/backend/src/services/aiService.ts +++ b/writing-platform/backend/src/services/aiService.ts @@ -6,7 +6,34 @@ const openai = new OpenAI({ }); // Per-user conversation history (in-memory; use Redis in production) -const conversationCache = new Map>(); +const MAX_CACHE_USERS = 1000; +const conversationCache = new Map; lastAccess: number }>(); + +// Evict least-recently-used entries when cache exceeds limit +function getHistory(userId: string): Array<{ role: 'user' | 'assistant'; content: string }> { + const entry = conversationCache.get(userId); + if (entry) { + entry.lastAccess = Date.now(); + return entry.messages; + } + return []; +} + +function setHistory(userId: string, messages: Array<{ role: 'user' | 'assistant'; content: string }>): void { + if (conversationCache.size >= MAX_CACHE_USERS) { + // Evict oldest entry + let oldestKey = ''; + let oldestTime = Infinity; + for (const [key, val] of conversationCache.entries()) { + if (val.lastAccess < oldestTime) { + oldestTime = val.lastAccess; + oldestKey = key; + } + } + if (oldestKey) conversationCache.delete(oldestKey); + } + conversationCache.set(userId, { messages, lastAccess: Date.now() }); +} export async function callAI(params: { systemPrompt: string; @@ -16,7 +43,7 @@ export async function callAI(params: { const { systemPrompt, userMessage, userId } = params; // Get recent conversation history (last 10 turns = 20 messages) - const history = conversationCache.get(userId) || []; + const history = getHistory(userId); const messages: Array<{ role: 'system' | 'user' | 'assistant'; content: string }> = [ { role: 'system', content: systemPrompt }, @@ -40,7 +67,7 @@ export async function callAI(params: { { role: 'user' as const, content: userMessage }, { role: 'assistant' as const, content: aiResponse }, ].slice(-20); - conversationCache.set(userId, updatedHistory); + setHistory(userId, updatedHistory); return aiResponse; } catch (err: any) { diff --git a/writing-platform/backend/src/services/smsService.ts b/writing-platform/backend/src/services/smsService.ts index 1593a752..0dbfff48 100644 --- a/writing-platform/backend/src/services/smsService.ts +++ b/writing-platform/backend/src/services/smsService.ts @@ -14,7 +14,8 @@ export async function sendVerificationCode(phone: string): Promise<{ success: bo // Check rate limit (1 code per 60 seconds per phone) const existing = codeStore.get(phone); - if (existing && existing.expiresAt - Date.now() > 4 * 60 * 1000) { + if (existing && (existing.expiresAt - Date.now()) > 4 * 60 * 1000) { + // Code was stored less than 60 seconds ago (5min total - 4min remaining = 1min elapsed) return { success: false, message: '验证码发送过于频繁,请稍后再试' }; } @@ -57,3 +58,13 @@ function storeCode(phone: string, code: string): void { expiresAt: Date.now() + 5 * 60 * 1000, // 5 minutes }); } + +// Cleanup expired codes periodically +setInterval(() => { + const now = Date.now(); + for (const [key, value] of codeStore.entries()) { + if (now > value.expiresAt) { + codeStore.delete(key); + } + } +}, 60000);