jedmund-svelte/src/routes/api/posts/+server.ts

202 lines
5.1 KiB
TypeScript

import type { RequestHandler } from './$types'
import { prisma } from '$lib/server/database'
import {
jsonResponse,
errorResponse,
getPaginationParams,
getPaginationMeta,
checkAdminAuth
} from '$lib/server/api-utils'
import { logger } from '$lib/server/logger'
import {
trackMediaUsage,
extractMediaIds,
type MediaUsageReference
} from '$lib/server/media-usage.js'
// GET /api/posts - List all posts
export const GET: RequestHandler = async (event) => {
if (!checkAdminAuth(event)) {
return errorResponse('Unauthorized', 401)
}
try {
const { page, limit } = getPaginationParams(event.url)
const skip = (page - 1) * limit
// Get filter parameters
const status = event.url.searchParams.get('status')
const postType = event.url.searchParams.get('postType')
// Build where clause
const where: any = {}
if (status) {
where.status = status
}
if (postType) {
where.postType = postType
}
// Get total count
const total = await prisma.post.count({ where })
// Get posts
const posts = await prisma.post.findMany({
where,
orderBy: { createdAt: 'desc' },
skip,
take: limit
})
const pagination = getPaginationMeta(total, page, limit)
logger.info('Posts list retrieved', { total, page, limit })
return jsonResponse({
posts,
pagination
})
} catch (error) {
logger.error('Failed to retrieve posts', error as Error)
return errorResponse('Failed to retrieve posts', 500)
}
}
// POST /api/posts - Create a new post
export const POST: RequestHandler = async (event) => {
if (!checkAdminAuth(event)) {
return errorResponse('Unauthorized', 401)
}
try {
const data = await event.request.json()
// Generate slug if not provided
if (!data.slug) {
if (data.title) {
// Generate slug from title
data.slug = data.title
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-+|-+$/g, '')
} else {
// Generate timestamp-based slug for posts without titles
data.slug = `post-${Date.now()}`
}
}
// Set publishedAt if status is published
if (data.status === 'published') {
data.publishedAt = new Date()
}
// Handle photo attachments for posts
let featuredImageId = data.featuredImage
if (data.attachedPhotos && data.attachedPhotos.length > 0 && !featuredImageId) {
// Use first attached photo as featured image for photo posts
featuredImageId = data.attachedPhotos[0]
}
// Handle album gallery - use first image as featured image
if (data.gallery && data.gallery.length > 0 && !featuredImageId) {
// Get the media URL for the first gallery item
const firstMedia = await prisma.media.findUnique({
where: { id: data.gallery[0] },
select: { url: true }
})
if (firstMedia) {
featuredImageId = firstMedia.url
}
}
// For albums, store gallery IDs in content field as a special structure
let postContent = data.content
if (data.type === 'album' && data.gallery) {
postContent = {
type: 'album',
gallery: data.gallery,
description: data.content
}
}
const post = await prisma.post.create({
data: {
title: data.title,
slug: data.slug,
postType: data.type,
status: data.status,
content: postContent,
linkUrl: data.link_url,
linkDescription: data.linkDescription,
featuredImage: featuredImageId,
attachments:
data.attachedPhotos && data.attachedPhotos.length > 0 ? data.attachedPhotos : null,
tags: data.tags,
publishedAt: data.publishedAt
}
})
// Track media usage
try {
const usageReferences: MediaUsageReference[] = []
// Track featured image
const featuredImageIds = extractMediaIds({ featuredImage: featuredImageId }, 'featuredImage')
featuredImageIds.forEach((mediaId) => {
usageReferences.push({
mediaId,
contentType: 'post',
contentId: post.id,
fieldName: 'featuredImage'
})
})
// Track attached photos (for photo posts)
if (data.attachedPhotos && Array.isArray(data.attachedPhotos)) {
data.attachedPhotos.forEach((mediaId: number) => {
usageReferences.push({
mediaId,
contentType: 'post',
contentId: post.id,
fieldName: 'attachments'
})
})
}
// Track gallery (for album posts)
if (data.gallery && Array.isArray(data.gallery)) {
data.gallery.forEach((mediaId: number) => {
usageReferences.push({
mediaId,
contentType: 'post',
contentId: post.id,
fieldName: 'gallery'
})
})
}
// Track media in post content
const contentIds = extractMediaIds({ content: postContent }, 'content')
contentIds.forEach((mediaId) => {
usageReferences.push({
mediaId,
contentType: 'post',
contentId: post.id,
fieldName: 'content'
})
})
if (usageReferences.length > 0) {
await trackMediaUsage(usageReferences)
}
} catch (error) {
logger.warn('Failed to track media usage for post', { postId: post.id, error })
}
logger.info('Post created', { id: post.id, title: post.title })
return jsonResponse(post)
} catch (error) {
logger.error('Failed to create post', error as Error)
return errorResponse('Failed to create post', 500)
}
}