Skip to content

Commit a0599e0

Browse files
committed
fix sst/tts api key forwarding
1 parent ae9dd5a commit a0599e0

4 files changed

Lines changed: 84 additions & 17 deletions

File tree

api-docs.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ Generates a response from an AI model. This is the core endpoint for chat functi
4444

4545
**Authentication**: Session or API Key
4646

47+
Notes:
48+
- If you authenticate with a NanoChat API key (`Authorization: Bearer nc_...`) or a session cookie, the server will use your stored NanoGPT key or the server `NANOGPT_API_KEY`.
49+
- To pass a NanoGPT key directly, send it via `x-api-key` or `Authorization: Bearer <nanogpt_key>` (non-`nc_`).
50+
4751
**Request Body**:
4852
```json
4953
{
@@ -106,6 +110,10 @@ Proxies requests to NanoGPT TTS API for speech synthesis.
106110

107111
**Authentication**: Session or API Key
108112

113+
Notes:
114+
- If you authenticate with a NanoChat API key (`Authorization: Bearer nc_...`) or a session cookie, the server will use your stored NanoGPT key or the server `NANOGPT_API_KEY`.
115+
- To pass a NanoGPT key directly, send it via `x-api-key` or `Authorization: Bearer <nanogpt_key>` (non-`nc_`).
116+
109117
**Request Body**:
110118
```json
111119
{
@@ -134,6 +142,10 @@ Polls the status of an asynchronous TTS request.
134142

135143
**Authentication**: Session or API Key
136144

145+
Notes:
146+
- If you authenticate with a NanoChat API key (`Authorization: Bearer nc_...`) or a session cookie, the server will use your stored NanoGPT key or the server `NANOGPT_API_KEY`.
147+
- To pass a NanoGPT key directly, send it via `x-api-key` or `Authorization: Bearer <nanogpt_key>` (non-`nc_`).
148+
137149
**Query Parameters**:
138150
- `runId`: (Required) The run ID returned by `/api/tts`.
139151
- `model`: (Required) The TTS model ID.
@@ -167,6 +179,10 @@ Proxies audio files to NanoGPT STT API for transcription.
167179

168180
**Authentication**: Session or API Key
169181

182+
Notes:
183+
- If you authenticate with a NanoChat API key (`Authorization: Bearer nc_...`) or a session cookie, the server will use your stored NanoGPT key or the server `NANOGPT_API_KEY`.
184+
- To pass a NanoGPT key directly, send it via `x-api-key` or `Authorization: Bearer <nanogpt_key>` (non-`nc_`).
185+
170186
**Request Body** (FormData):
171187
- `audio`: Binary audio file (webm, mp4, etc).
172188
- `model`: "string (optional, default: 'Whisper-Large-V3')"

src/routes/api/stt/+server.ts

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,39 @@ import { db } from '$lib/db';
55
import { modelPerformanceStats } from '$lib/db/schema';
66
import { eq, and, sql } from 'drizzle-orm';
77
import { tryGetAuthenticatedUserId } from '$lib/backend/auth-utils';
8+
import { getUserKey } from '$lib/db/queries';
89

910
const COST_PER_MINUTE: Record<string, number> = {
1011
'Whisper-Large-V3': 0.01,
1112
Wizper: 0.01,
1213
'Elevenlabs-STT': 0.03,
1314
};
1415

15-
const getApiKey = (request: Request): string | null => {
16+
const getExplicitNanoGPTKey = (request: Request): string | null => {
17+
const headerKey = request.headers.get('x-api-key');
18+
if (headerKey && !headerKey.startsWith('nc_')) {
19+
return headerKey;
20+
}
21+
1622
const authHeader = request.headers.get('Authorization');
1723
if (authHeader?.startsWith('Bearer ')) {
1824
const token = authHeader.slice(7).trim();
19-
if (token.length > 0) {
25+
if (token.length > 0 && !token.startsWith('nc_')) {
2026
return token;
2127
}
2228
}
2329

24-
return request.headers.get('x-api-key') || env.NANOGPT_API_KEY || null;
30+
return null;
31+
};
32+
33+
const resolveNanoGPTKey = async (request: Request, userId?: string): Promise<string | null> => {
34+
const explicitKey = getExplicitNanoGPTKey(request);
35+
if (explicitKey) return explicitKey;
36+
37+
if (!userId) return null;
38+
39+
const userKey = await getUserKey(userId, 'nanogpt');
40+
return userKey || env.NANOGPT_API_KEY || null;
2541
};
2642

2743
export const POST: RequestHandler = async ({ request, fetch }) => {
@@ -43,10 +59,11 @@ export const POST: RequestHandler = async ({ request, fetch }) => {
4359
formData.set('language', language);
4460
}
4561

46-
const apiKey = getApiKey(request);
62+
const userId = await tryGetAuthenticatedUserId(request);
63+
const apiKey = await resolveNanoGPTKey(request, userId);
4764

4865
if (!apiKey) {
49-
return json({ error: 'API key is required' }, { status: 401 });
66+
return json({ error: 'Authentication required or NanoGPT API key missing' }, { status: 401 });
5067
}
5168

5269
const start = Date.now();
@@ -82,7 +99,6 @@ export const POST: RequestHandler = async ({ request, fetch }) => {
8299
// Track analytics asynchronously
83100
(async () => {
84101
try {
85-
const userId = await tryGetAuthenticatedUserId(request);
86102
if (userId) {
87103
const durationMs = Date.now() - start;
88104

src/routes/api/tts/+server.ts

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { db } from '$lib/db';
55
import { modelPerformanceStats } from '$lib/db/schema';
66
import { eq, and, sql } from 'drizzle-orm';
77
import { tryGetAuthenticatedUserId } from '$lib/backend/auth-utils';
8+
import { getUserKey } from '$lib/db/queries';
89

910
const COST_PER_1K_CHARS: Record<string, number> = {
1011
'gpt-4o-mini-tts': 0.0125,
@@ -17,16 +18,31 @@ const COST_PER_1K_CHARS: Record<string, number> = {
1718
// Default to standard price if unknown
1819
const DEFAULT_COST = 0.015;
1920

20-
const getApiKey = (request: Request): string | null => {
21+
const getExplicitNanoGPTKey = (request: Request): string | null => {
22+
const headerKey = request.headers.get('x-api-key');
23+
if (headerKey && !headerKey.startsWith('nc_')) {
24+
return headerKey;
25+
}
26+
2127
const authHeader = request.headers.get('Authorization');
2228
if (authHeader?.startsWith('Bearer ')) {
2329
const token = authHeader.slice(7).trim();
24-
if (token.length > 0) {
30+
if (token.length > 0 && !token.startsWith('nc_')) {
2531
return token;
2632
}
2733
}
2834

29-
return request.headers.get('x-api-key') || env.NANOGPT_API_KEY || null;
35+
return null;
36+
};
37+
38+
const resolveNanoGPTKey = async (request: Request, userId?: string): Promise<string | null> => {
39+
const explicitKey = getExplicitNanoGPTKey(request);
40+
if (explicitKey) return explicitKey;
41+
42+
if (!userId) return null;
43+
44+
const userKey = await getUserKey(userId, 'nanogpt');
45+
return userKey || env.NANOGPT_API_KEY || null;
3046
};
3147

3248
export const POST: RequestHandler = async ({ request, fetch }) => {
@@ -43,10 +59,11 @@ export const POST: RequestHandler = async ({ request, fetch }) => {
4359
return json({ error: 'Text is required' }, { status: 400 });
4460
}
4561

46-
const apiKey = getApiKey(request);
62+
const userId = await tryGetAuthenticatedUserId(request);
63+
const apiKey = await resolveNanoGPTKey(request, userId);
4764

4865
if (!apiKey) {
49-
return json({ error: 'API key is required' }, { status: 401 });
66+
return json({ error: 'Authentication required or NanoGPT API key missing' }, { status: 401 });
5067
}
5168

5269
const start = Date.now();
@@ -89,7 +106,6 @@ export const POST: RequestHandler = async ({ request, fetch }) => {
89106
// Track analytics asynchronously
90107
(async () => {
91108
try {
92-
const userId = await tryGetAuthenticatedUserId(request);
93109
if (userId) {
94110
console.log(`[TTS] Tracking analytics for user: ${userId}, model: ${model}`);
95111
const duration = Date.now() - start;

src/routes/api/tts/status/+server.ts

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,42 @@
1+
import { env } from '$env/dynamic/private';
12
import { json } from '@sveltejs/kit';
23
import type { RequestHandler } from './$types';
4+
import { tryGetAuthenticatedUserId } from '$lib/backend/auth-utils';
5+
import { getUserKey } from '$lib/db/queries';
6+
7+
const getExplicitNanoGPTKey = (request: Request): string | null => {
8+
const headerKey = request.headers.get('x-api-key');
9+
if (headerKey && !headerKey.startsWith('nc_')) {
10+
return headerKey;
11+
}
312

4-
const getApiKey = (request: Request): string | null => {
513
const authHeader = request.headers.get('Authorization');
614
if (authHeader?.startsWith('Bearer ')) {
715
const token = authHeader.slice(7).trim();
8-
if (token.length > 0) {
16+
if (token.length > 0 && !token.startsWith('nc_')) {
917
return token;
1018
}
1119
}
1220

13-
return request.headers.get('x-api-key') || env.NANOGPT_API_KEY || null;
21+
return null;
22+
};
23+
24+
const resolveNanoGPTKey = async (request: Request, userId?: string): Promise<string | null> => {
25+
const explicitKey = getExplicitNanoGPTKey(request);
26+
if (explicitKey) return explicitKey;
27+
28+
if (!userId) return null;
29+
30+
const userKey = await getUserKey(userId, 'nanogpt');
31+
return userKey || env.NANOGPT_API_KEY || null;
1432
};
1533

1634
export const GET: RequestHandler = async ({ request, fetch, url }) => {
17-
const apiKey = getApiKey(request);
35+
const userId = await tryGetAuthenticatedUserId(request);
36+
const apiKey = await resolveNanoGPTKey(request, userId);
1837

1938
if (!apiKey) {
20-
return json({ error: 'API key is required' }, { status: 401 });
39+
return json({ error: 'Authentication required or NanoGPT API key missing' }, { status: 401 });
2140
}
2241

2342
const runId = url.searchParams.get('runId');

0 commit comments

Comments
 (0)