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);