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
This commit is contained in:
copilot-swe-agent[bot] 2026-03-25 13:05:31 +00:00
parent e860b9963b
commit 403fc62e84
4 changed files with 52 additions and 7 deletions

View File

@ -5,7 +5,7 @@ const requestCounts = new Map<string, { count: number; resetTime: number }>();
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);

View File

@ -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<void> => {
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,
},

View File

@ -6,7 +6,34 @@ const openai = new OpenAI({
});
// Per-user conversation history (in-memory; use Redis in production)
const conversationCache = new Map<string, Array<{ role: 'user' | 'assistant'; content: string }>>();
const MAX_CACHE_USERS = 1000;
const conversationCache = new Map<string, { messages: Array<{ role: 'user' | 'assistant'; content: string }>; 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) {

View File

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