@kaifronsdal/transcript-viewer
Version:
A web-based viewer for AI conversation transcripts with rollback support
101 lines (86 loc) • 3.05 kB
text/typescript
import { json, error } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
import path from 'path';
import { validateFilePath, normalizeFilePath } from '$lib/server/utils/server-file-utils';
import { getTranscriptCache } from '$lib/server/cache/transcript-cache';
import { TRANSCRIPT_DIR } from '$lib/server/config';
export const GET: RequestHandler = async ({ url }) => {
try {
// Parse query parameters
const filePath = url.searchParams.get('filePath');
const metadataOnly = url.searchParams.get('metadataOnly') === 'true';
// Validate required parameters
if (!filePath) {
throw error(400, {
message: 'Missing required parameter: filePath'
});
}
// All file paths should now be relative to the transcript directory
// Resolve the relative path against the transcript directory
const resolvedPath = path.resolve(TRANSCRIPT_DIR, filePath);
// Validate the relative file path
if (!validateFilePath(filePath)) {
throw error(400, {
message: `Invalid or unsafe file path: ${filePath}`
});
}
// Get cache instance
const cache = getTranscriptCache();
// Load the requested data through cache
if (metadataOnly) {
const metadata = await cache.getMetadata(resolvedPath);
if (metadata === null) {
throw error(404, {
message: `Transcript not found: ${filePath}`
});
}
return json({
success: true,
data: metadata
}, {
headers: {
'Cache-Control': 'public, max-age=300', // 5 minutes browser cache for metadata
}
});
} else {
const transcript = await cache.getFullTranscript(resolvedPath);
if (transcript === null) {
throw error(404, {
message: `Transcript not found: ${filePath}`
});
}
return json({
success: true,
data: transcript
}, {
headers: {
'Cache-Control': 'public, max-age=60', // 1 minute browser cache for full transcripts
}
});
}
} catch (err: any) {
// If it's already a SvelteKit error, re-throw it
if (err.status) {
throw err;
}
// Validation failure mapping with details
if (err?.code === 'TRANSCRIPT_VALIDATION_FAILED' && err.validation) {
const { errors: validationErrors } = err.validation;
const details = validationErrors?.map?.((e: any, i: number) => {
const path = e.path || 'root';
return `${i + 1}. ${path}: ${e.message}`;
})?.join('\n');
console.error('Transcript validation error:', details);
throw error(422, {
message: 'Transcript schema validation failed',
details,
} as any);
}
// Log unexpected errors
console.error('Transcript API error:', err);
// Return generic 500 error for unexpected issues
throw error(500, {
message: 'Internal server error while loading transcript'
});
}
};