Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions server/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,15 @@ describe('CORS configuration', () => {
expect(res.status).not.toBe(500);
});

it('rejects missing origin in production mode', async () => {
it('allows missing origin in production mode (for health checks etc)', async () => {
env.NODE_ENV = 'production';

const originalWarn = console.warn;
console.warn = vi.fn();

const res = await request(app).get('/health');
// The error handler in index.ts catches the Error thrown by CORS
// and returns a 500 status with a generic "Internal server error" message.
expect(res.status).toBe(500);
expect(res.body.message).toBe('Internal server error');
// It should not throw a CORS error
expect(res.status).not.toBe(500);

console.warn = originalWarn;
});
Expand Down
8 changes: 2 additions & 6 deletions server/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,9 @@ const allowedOrigins = env.ALLOWED_ORIGINS.split(',')
app.use(
cors({
origin: (origin, callback) => {
// allow server-to-server / curl requests in development, reject in production
// allow server-to-server / curl requests and health checks
if (!origin) {
if (env.NODE_ENV !== 'production') {
return callback(null, true);
}
console.warn(`[CORS] Rejected Missing Origin in production`);
return callback(new Error(`CORS: missing origin not allowed`));
return callback(null, true);
}

if (allowedOrigins.includes(origin)) return callback(null, true);
Expand Down
12 changes: 3 additions & 9 deletions server/src/utils/embeddings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,19 +134,13 @@ describe('embeddings util', () => {
expect(result).toEqual([]);
});

// Because '[]' won't throw until it gets returned back out, wait...
// Actually `generateEmbeddings` expects response.data to exist and loops over it.
// If `response.data` is empty, `response.data` is `[]`.
// `[...response.data]` is `[]`, mapped is `[]`.
// Returned is `[]`.
// `generateEmbedding` will then do: `const [embedding] = await generateEmbeddings(['valid text']);`
// `embedding` will be `undefined`.
// And throw `AppError(500, 'Embedding generation returned no result.')`.
it('should throw AppError with status 502 on single API error', async () => {
mockCreate.mockRejectedValueOnce(new Error('API failure'));

const error = await generateEmbedding('valid text').catch((e) => e);
expect(error).toBeInstanceOf(AppError);
expect(error.statusCode).toBe(502);
expect(error.message).toContain('Embedding generation failed');
expect(error.message).toBe('Embedding generation failed: API failure');
});
});
});
8 changes: 5 additions & 3 deletions server/src/utils/embeddings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import { getOpenRouterClient } from '../lib/openrouter.js';
import { AppError } from './AppError.js';
import { logger } from './logger.js';

/** Embedding model — must be hosted on OpenRouter or a compatible endpoint */
const EMBEDDING_MODEL = 'openai/text-embedding-3-small';
Expand Down Expand Up @@ -43,7 +44,7 @@ export async function generateEmbeddings(texts: string[]): Promise<number[][]> {
err instanceof Error
? err.message
: 'Unknown error generating embeddings';
console.error('[Embeddings] API call failed:', message);
logger.error(`[Embeddings] API call failed: ${message}`);
throw new AppError(502, `Embedding generation failed: ${message}`);
}
}
Expand All @@ -52,8 +53,9 @@ export async function generateEmbeddings(texts: string[]): Promise<number[][]> {
*/
export async function generateEmbedding(text: string): Promise<number[]> {
const result = await generateEmbeddings([text]);
if (result.length === 0) {
const embedding = result[0];
if (!embedding) {
throw new AppError(500, 'Embedding generation returned no result.');
}
return result[0];
return embedding;
}