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:
parent
e860b9963b
commit
403fc62e84
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue