Skip to content

Commit 5841d0d

Browse files
author
rajiv_rago
committed
fix(security): Sanitize AI route error messages and delete debug endpoint
Stop leaking internal details (provider names, error.message) in API responses. Provider-not-configured errors now return 503 with a generic message and log details server-side via captureException. Catch-all blocks return generic 500s. Handler throw messages are also sanitized since they surface via job status polling. Deleted the temporary /api/debug/env diagnostic endpoint.
1 parent 2eb3d8c commit 5841d0d

8 files changed

Lines changed: 57 additions & 87 deletions

File tree

app/api/courses/ai/[courseId]/generate-all/route.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,12 @@ export async function POST(
102102
course.aiPreferences?.defaultProvider ||
103103
process.env.AI_PROVIDER ||
104104
"openai";
105+
captureException(new Error(`Provider not configured: ${requestedProvider}`), {
106+
operation: "resolve-provider",
107+
});
105108
return NextResponse.json(
106-
{ error: `API key not configured for provider: ${requestedProvider}` },
107-
{ status: 500 }
109+
{ error: "AI service is temporarily unavailable. Please try again later." },
110+
{ status: 503 }
108111
);
109112
}
110113

@@ -136,9 +139,7 @@ export async function POST(
136139
} catch (error) {
137140
captureException(error, { operation: "Generate all modules content error" });
138141
return NextResponse.json(
139-
{
140-
error: `Failed to generate content: ${error instanceof Error ? error.message : "Unknown error"}`,
141-
},
142+
{ error: "Something went wrong. Please try again later." },
142143
{ status: 500 }
143144
);
144145
}

app/api/courses/ai/[courseId]/lessons/[lessonId]/generate/route.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,12 @@ export async function POST(
103103
course.aiPreferences?.defaultProvider ||
104104
process.env.AI_PROVIDER ||
105105
"openai";
106+
captureException(new Error(`Provider not configured: ${requestedProvider}`), {
107+
operation: "resolve-provider",
108+
});
106109
return NextResponse.json(
107-
{ error: `API key not configured for provider: ${requestedProvider}` },
108-
{ status: 500 }
110+
{ error: "AI service is temporarily unavailable. Please try again later." },
111+
{ status: 503 }
109112
);
110113
}
111114

@@ -128,9 +131,7 @@ export async function POST(
128131
} catch (error) {
129132
captureException(error, { operation: "Generate lesson content error" });
130133
return NextResponse.json(
131-
{
132-
error: `Failed to generate content: ${error instanceof Error ? error.message : "Unknown error"}`,
133-
},
134+
{ error: "Something went wrong. Please try again later." },
134135
{ status: 500 }
135136
);
136137
}

app/api/courses/ai/[courseId]/modules/[moduleId]/generate/route.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,12 @@ export async function POST(
9898
course.aiPreferences?.defaultProvider ||
9999
process.env.AI_PROVIDER ||
100100
"openai";
101+
captureException(new Error(`Provider not configured: ${requestedProvider}`), {
102+
operation: "resolve-provider",
103+
});
101104
return NextResponse.json(
102-
{ error: `API key not configured for provider: ${requestedProvider}` },
103-
{ status: 500 }
105+
{ error: "AI service is temporarily unavailable. Please try again later." },
106+
{ status: 503 }
104107
);
105108
}
106109

@@ -122,9 +125,7 @@ export async function POST(
122125
} catch (error) {
123126
captureException(error, { operation: "Generate module content error" });
124127
return NextResponse.json(
125-
{
126-
error: `Failed to generate content: ${error instanceof Error ? error.message : "Unknown error"}`,
127-
},
128+
{ error: "Something went wrong. Please try again later." },
128129
{ status: 500 }
129130
);
130131
}

app/api/courses/ai/syllabus/route.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,13 @@ export async function POST(request: NextRequest) {
6464
});
6565

6666
if (!resolved) {
67-
const selectedProvider = provider || process.env.AI_PROVIDER || "openai";
67+
const requestedProvider = provider || process.env.AI_PROVIDER || "openai";
68+
captureException(new Error(`Provider not configured: ${requestedProvider}`), {
69+
operation: "resolve-provider",
70+
});
6871
return NextResponse.json(
69-
{ error: `API key not configured for provider: ${selectedProvider}` },
70-
{ status: 500 }
72+
{ error: "AI service is temporarily unavailable. Please try again later." },
73+
{ status: 503 }
7174
);
7275
}
7376

@@ -82,11 +85,8 @@ export async function POST(request: NextRequest) {
8285
return jsonResponse;
8386
} catch (error) {
8487
captureException(error, { operation: "Create syllabus error" });
85-
86-
const errorMessage = error instanceof Error ? error.message : "Unknown error";
87-
8888
return NextResponse.json(
89-
{ error: `Failed to generate syllabus: ${errorMessage}` },
89+
{ error: "Something went wrong. Please try again later." },
9090
{ status: 500 }
9191
);
9292
}

app/api/courses/youtube/generate/route.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,13 @@ export async function POST(request: NextRequest) {
5656
});
5757

5858
if (!resolved) {
59-
const selectedProvider = provider || process.env.AI_PROVIDER || "openai";
59+
const requestedProvider = provider || process.env.AI_PROVIDER || "openai";
60+
captureException(new Error(`Provider not configured: ${requestedProvider}`), {
61+
operation: "resolve-provider",
62+
});
6063
return NextResponse.json(
61-
{ error: `API key not configured for provider: ${selectedProvider}` },
62-
{ status: 500 }
64+
{ error: "AI service is temporarily unavailable. Please try again later." },
65+
{ status: 503 }
6366
);
6467
}
6568

@@ -74,11 +77,8 @@ export async function POST(request: NextRequest) {
7477
return jsonResponse;
7578
} catch (error) {
7679
captureException(error, { operation: "Generate YouTube path error" });
77-
78-
const errorMessage = error instanceof Error ? error.message : "Unknown error";
79-
8080
return NextResponse.json(
81-
{ error: `Failed to generate YouTube path: ${errorMessage}` },
81+
{ error: "Something went wrong. Please try again later." },
8282
{ status: 500 }
8383
);
8484
}

app/api/debug/env/route.ts

Lines changed: 0 additions & 50 deletions
This file was deleted.

lib/queue/handlers/aiGeneration.ts

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,11 @@ registerHandler(
5656
});
5757

5858
if (!resolved) {
59-
throw new Error(
60-
`API key not configured for provider: ${provider || process.env.AI_PROVIDER || "openai"}`
61-
);
59+
captureException(new Error(`Provider not configured: ${provider || process.env.AI_PROVIDER || "openai"}`), {
60+
operation: "resolve-provider",
61+
jobType: "ai.generate-syllabus",
62+
});
63+
throw new Error("AI service is temporarily unavailable");
6264
}
6365

6466
await dbConnect();
@@ -195,7 +197,13 @@ registerHandler(
195197
userPreferences,
196198
});
197199

198-
if (!resolved) throw new Error("API key not configured");
200+
if (!resolved) {
201+
captureException(new Error(`Provider not configured: ${provider || "default"}`), {
202+
operation: "resolve-provider",
203+
jobType: "ai.generate-module-content",
204+
});
205+
throw new Error("AI service is temporarily unavailable");
206+
}
199207

200208
const courseModule = await Module.findOne({
201209
_id: moduleId,
@@ -378,7 +386,13 @@ registerHandler(
378386
userPreferences,
379387
});
380388

381-
if (!resolved) throw new Error("API key not configured");
389+
if (!resolved) {
390+
captureException(new Error(`Provider not configured: ${provider || "default"}`), {
391+
operation: "resolve-provider",
392+
jobType: "ai.generate-lesson-content",
393+
});
394+
throw new Error("AI service is temporarily unavailable");
395+
}
382396

383397
const lessonService = new LessonContentGeneratorService({
384398
provider: resolved.provider,

lib/queue/handlers/youtubeGeneration.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { YouTubePathService } from "@/lib/youtube";
77
import type { YouTubePathFormData } from "@/lib/youtube";
88
import { logAIGeneration } from "@/lib/utils/aiGenerationLogger";
99
import { sendNotification } from "@/lib/notifications";
10+
import { captureException } from "@/lib/logger";
1011
import { env } from "@/lib/env";
1112
import { registerHandler } from "./index";
1213

@@ -52,9 +53,11 @@ registerHandler(
5253
});
5354

5455
if (!resolved) {
55-
throw new Error(
56-
`API key not configured for provider: ${provider || process.env.AI_PROVIDER || "openai"}`
57-
);
56+
captureException(new Error(`Provider not configured: ${provider || process.env.AI_PROVIDER || "openai"}`), {
57+
operation: "resolve-provider",
58+
jobType: "ai.generate-youtube-path",
59+
});
60+
throw new Error("AI service is temporarily unavailable");
5861
}
5962

6063
await dbConnect();

0 commit comments

Comments
 (0)