Files
taptapp/src/routes/api/audio/[id]/+server.js
2025-11-10 22:51:01 +01:00

116 lines
3.1 KiB
JavaScript

import { error } from '@sveltejs/kit';
import { db } from '$lib/server/db/index.js';
import { audioFile, inviteLink, participant } from '$lib/server/db/schema.js';
import { eq, isNull, and } from 'drizzle-orm';
import { getFromS3, getFromS3WithRange } from '$lib/server/s3.js';
import { parseStoredTags, matchesInviteTags } from '$lib/server/tag-utils.js';
export async function GET({ params, request, url, cookies }) {
const fileId = params.id;
const token = url.searchParams.get('token');
if (!token) {
throw error(400, 'Missing token');
}
const participantId = cookies.get(`participant-${token}`);
if (!participantId) {
throw error(403, 'Unauthorized');
}
const participants = await db
.select({ inviteToken: participant.inviteToken })
.from(participant)
.where(
and(
eq(participant.id, participantId),
eq(participant.inviteToken, token),
isNull(participant.deletedAt)
)
);
if (participants.length === 0) {
throw error(403, 'Unauthorized');
}
const invites = await db
.select({ tags: inviteLink.tags })
.from(inviteLink)
.where(
and(
eq(inviteLink.token, token),
isNull(inviteLink.deletedAt)
)
);
if (invites.length === 0) {
throw error(404, 'Invite not found');
}
const inviteTags = parseStoredTags(invites[0].tags);
// Get file metadata from database (only s3Key and contentType needed)
const files = await db.select({
s3Key: audioFile.s3Key,
contentType: audioFile.contentType,
fileSize: audioFile.fileSize,
tags: audioFile.tags
})
.from(audioFile)
.where(and(
eq(audioFile.id, fileId),
isNull(audioFile.deletedAt) // Only serve active audio files
));
if (files.length === 0) {
throw error(404, 'Audio file not found');
}
const file = files[0];
const audioTags = parseStoredTags(file.tags);
if (!matchesInviteTags(audioTags, inviteTags)) {
throw error(403, 'Unauthorized');
}
// Check if file has S3 key (new files) or fall back to error for old blob-based files
if (!file.s3Key) {
console.error(`Audio file ${fileId} missing S3 key - may be an old blob-based file`);
throw error(500, 'Audio file not available - please re-upload');
}
const range = request.headers.get('range');
try {
if (range) {
// Handle range requests for audio streaming
const s3Response = await getFromS3WithRange(file.s3Key, range);
return new Response(s3Response.stream, {
status: 206,
headers: {
'Content-Range': s3Response.contentRange,
'Accept-Ranges': s3Response.acceptRanges || 'bytes',
'Content-Length': s3Response.contentLength.toString(),
'Content-Type': s3Response.contentType || file.contentType || 'audio/mpeg'
}
});
} else {
// Handle regular requests
const s3Response = await getFromS3(file.s3Key);
return new Response(s3Response.stream, {
headers: {
'Content-Type': s3Response.contentType || file.contentType || 'audio/mpeg',
'Content-Length': (s3Response.contentLength || file.fileSize || 0).toString(),
'Accept-Ranges': 'bytes',
'Cache-Control': 'public, max-age=3600'
}
});
}
} catch (s3Error) {
console.error('Error fetching from S3:', s3Error);
throw error(500, 'Failed to fetch audio file');
}
}