jedmund-svelte/src/routes/api/albums/[id]/media/+server.ts
Justin Edmund 003e08836e feat(api): add album content support and media management endpoints
- Update album CRUD endpoints to handle content field
- Add /api/albums/[id]/media endpoint for managing album media
- Add /api/media/[id]/albums endpoint for media-album associations
- Create album routes for public album viewing
- Update album queries to use new MediaAlbum join table
- Support filtering and sorting in album listings

Enables rich content albums with flexible media associations.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-24 01:11:30 +01:00

135 lines
3.4 KiB
TypeScript

import type { RequestHandler } from './$types'
import { prisma } from '$lib/server/database'
import { jsonResponse, errorResponse, checkAdminAuth } from '$lib/server/api-utils'
import { logger } from '$lib/server/logger'
// POST /api/albums/[id]/media - Add media to album (bulk operation)
export const POST: RequestHandler = async (event) => {
// Check admin auth
if (!checkAdminAuth(event)) {
return errorResponse('Unauthorized', 401)
}
try {
const albumId = parseInt(event.params.id)
const body = await event.request.json()
const { mediaIds } = body
if (!Array.isArray(mediaIds) || mediaIds.length === 0) {
return errorResponse('Media IDs are required', 400)
}
// Check if album exists
const album = await prisma.album.findUnique({
where: { id: albumId }
})
if (!album) {
return errorResponse('Album not found', 404)
}
// Get current max display order
const maxOrderResult = await prisma.albumMedia.findFirst({
where: { albumId },
orderBy: { displayOrder: 'desc' },
select: { displayOrder: true }
})
let currentOrder = maxOrderResult?.displayOrder || 0
// Create album-media associations
const albumMediaData = mediaIds.map((mediaId: number) => ({
albumId,
mediaId,
displayOrder: ++currentOrder
}))
// Use createMany with skipDuplicates to avoid errors if media already in album
await prisma.albumMedia.createMany({
data: albumMediaData,
skipDuplicates: true
})
// Get updated count
const updatedCount = await prisma.albumMedia.count({
where: { albumId }
})
return jsonResponse({
message: 'Media added to album successfully',
mediaCount: updatedCount
})
} catch (error) {
logger.error('Failed to add media to album', error as Error)
return errorResponse('Failed to add media to album', 500)
}
}
// DELETE /api/albums/[id]/media - Remove media from album (bulk operation)
export const DELETE: RequestHandler = async (event) => {
// Check admin auth
if (!checkAdminAuth(event)) {
return errorResponse('Unauthorized', 401)
}
try {
const albumId = parseInt(event.params.id)
const body = await event.request.json()
const { mediaIds } = body
if (!Array.isArray(mediaIds) || mediaIds.length === 0) {
return errorResponse('Media IDs are required', 400)
}
// Check if album exists
const album = await prisma.album.findUnique({
where: { id: albumId }
})
if (!album) {
return errorResponse('Album not found', 404)
}
// Delete album-media associations
await prisma.albumMedia.deleteMany({
where: {
albumId,
mediaId: { in: mediaIds }
}
})
// Get updated count
const updatedCount = await prisma.albumMedia.count({
where: { albumId }
})
// Reorder remaining media to fill gaps
const remainingMedia = await prisma.albumMedia.findMany({
where: { albumId },
orderBy: { displayOrder: 'asc' }
})
// Update display order to remove gaps
for (let i = 0; i < remainingMedia.length; i++) {
if (remainingMedia[i].displayOrder !== i + 1) {
await prisma.albumMedia.update({
where: {
albumId_mediaId: {
albumId: remainingMedia[i].albumId,
mediaId: remainingMedia[i].mediaId
}
},
data: { displayOrder: i + 1 }
})
}
}
return jsonResponse({
message: 'Media removed from album successfully',
mediaCount: updatedCount
})
} catch (error) {
logger.error('Failed to remove media from album', error as Error)
return errorResponse('Failed to remove media from album', 500)
}
}