mirror of
https://github.com/DayuanJiang/next-ai-draw-io.git
synced 2026-01-02 14:22:28 +08:00
fix: use full IP for userId to prevent quota collision (#400)
* fix: use full IP for userId to prevent quota collision - Remove .slice(0, 8) from base64 encoded IP - Each IP now has unique userId (no /16 collision) - Affects: quota tracking, Langfuse tracing * refactor: extract getUserIdFromRequest to shared utility - Create lib/user-id.ts with shared function - Fix misleading 'privacy' comment (base64 is not privacy) - Remove duplicate code from chat and log-feedback routes
This commit is contained in:
@@ -26,6 +26,7 @@ import {
|
|||||||
wrapWithObserve,
|
wrapWithObserve,
|
||||||
} from "@/lib/langfuse"
|
} from "@/lib/langfuse"
|
||||||
import { getSystemPrompt } from "@/lib/system-prompts"
|
import { getSystemPrompt } from "@/lib/system-prompts"
|
||||||
|
import { getUserIdFromRequest } from "@/lib/user-id"
|
||||||
|
|
||||||
export const maxDuration = 120
|
export const maxDuration = 120
|
||||||
|
|
||||||
@@ -167,13 +168,8 @@ async function handleChatRequest(req: Request): Promise<Response> {
|
|||||||
|
|
||||||
const { messages, xml, previousXml, sessionId } = await req.json()
|
const { messages, xml, previousXml, sessionId } = await req.json()
|
||||||
|
|
||||||
// Get user IP for Langfuse tracking (hashed for privacy)
|
// Get user ID for Langfuse tracking and quota
|
||||||
const forwardedFor = req.headers.get("x-forwarded-for")
|
const userId = getUserIdFromRequest(req)
|
||||||
const rawIp = forwardedFor?.split(",")[0]?.trim() || "anonymous"
|
|
||||||
const userId =
|
|
||||||
rawIp === "anonymous"
|
|
||||||
? rawIp
|
|
||||||
: `user-${Buffer.from(rawIp).toString("base64url").slice(0, 8)}`
|
|
||||||
|
|
||||||
// Validate sessionId for Langfuse (must be string, max 200 chars)
|
// Validate sessionId for Langfuse (must be string, max 200 chars)
|
||||||
const validSessionId =
|
const validSessionId =
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { randomUUID } from "crypto"
|
import { randomUUID } from "crypto"
|
||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
import { getLangfuseClient } from "@/lib/langfuse"
|
import { getLangfuseClient } from "@/lib/langfuse"
|
||||||
|
import { getUserIdFromRequest } from "@/lib/user-id"
|
||||||
|
|
||||||
const feedbackSchema = z.object({
|
const feedbackSchema = z.object({
|
||||||
messageId: z.string().min(1).max(200),
|
messageId: z.string().min(1).max(200),
|
||||||
@@ -32,13 +33,8 @@ export async function POST(req: Request) {
|
|||||||
return Response.json({ success: true, logged: false })
|
return Response.json({ success: true, logged: false })
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get user IP for tracking (hashed for privacy)
|
// Get user ID for tracking
|
||||||
const forwardedFor = req.headers.get("x-forwarded-for")
|
const userId = getUserIdFromRequest(req)
|
||||||
const rawIp = forwardedFor?.split(",")[0]?.trim() || "anonymous"
|
|
||||||
const userId =
|
|
||||||
rawIp === "anonymous"
|
|
||||||
? rawIp
|
|
||||||
: `user-${Buffer.from(rawIp).toString("base64url").slice(0, 8)}`
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Find the most recent chat trace for this session to attach the score to
|
// Find the most recent chat trace for this session to attach the score to
|
||||||
|
|||||||
12
lib/user-id.ts
Normal file
12
lib/user-id.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* Generate a userId from request for tracking purposes.
|
||||||
|
* Uses base64url encoding of IP for URL-safe identifier.
|
||||||
|
* Note: base64 is reversible - this is NOT privacy protection.
|
||||||
|
*/
|
||||||
|
export function getUserIdFromRequest(req: Request): string {
|
||||||
|
const forwardedFor = req.headers.get("x-forwarded-for")
|
||||||
|
const rawIp = forwardedFor?.split(",")[0]?.trim() || "anonymous"
|
||||||
|
return rawIp === "anonymous"
|
||||||
|
? rawIp
|
||||||
|
: `user-${Buffer.from(rawIp).toString("base64url")}`
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user