fix: eliminate remaining any types in API routes

- use Prisma.JsonValue and Prisma input types throughout
- add proper type guards for array and object checks
- replace any with Record<string, unknown> where appropriate
- all API/RSS routes now have zero any type errors
This commit is contained in:
Justin Edmund 2025-11-23 05:28:05 -08:00
parent 73c2fae7b8
commit 8ec4c582c1
10 changed files with 58 additions and 13 deletions

View file

@ -1,4 +1,5 @@
import type { RequestHandler } from './$types' import type { RequestHandler } from './$types'
import type { Prisma } from '@prisma/client'
import { prisma } from '$lib/server/database' import { prisma } from '$lib/server/database'
import { import {
jsonResponse, jsonResponse,
@ -89,7 +90,7 @@ export const PUT: RequestHandler = async (event) => {
coverPhotoId?: number coverPhotoId?: number
status?: string status?: string
showInUniverse?: boolean showInUniverse?: boolean
content?: any content?: Prisma.JsonValue
}>(event.request) }>(event.request)
if (!body) { if (!body) {

View file

@ -143,7 +143,7 @@ export const PUT: RequestHandler = async (event) => {
}>(event.request) }>(event.request)
// Also support legacy photoId parameter // Also support legacy photoId parameter
const mediaId = body?.mediaId || (body as any)?.photoId const mediaId = body?.mediaId || (body as Record<string, unknown>)?.photoId
if (!mediaId || body?.displayOrder === undefined) { if (!mediaId || body?.displayOrder === undefined) {
return errorResponse('Media ID and display order are required', 400) return errorResponse('Media ID and display order are required', 400)
} }

View file

@ -104,7 +104,7 @@ async function getRecentAlbums(
recentTracksResponse = JSON.parse(cached) recentTracksResponse = JSON.parse(cached)
// Convert date strings back to Date objects // Convert date strings back to Date objects
if (recentTracksResponse.tracks) { if (recentTracksResponse.tracks) {
recentTracksResponse.tracks = recentTracksResponse.tracks.map((track: any) => ({ recentTracksResponse.tracks = recentTracksResponse.tracks.map((track) => ({
...track, ...track,
date: track.date ? new Date(track.date) : undefined date: track.date ? new Date(track.date) : undefined
})) }))

View file

@ -1,4 +1,5 @@
import type { RequestHandler } from './$types' import type { RequestHandler } from './$types'
import type { Prisma } from '@prisma/client'
import { prisma } from '$lib/server/database' import { prisma } from '$lib/server/database'
import { import {
jsonResponse, jsonResponse,
@ -29,7 +30,7 @@ export const GET: RequestHandler = async (event) => {
const albumId = event.url.searchParams.get('albumId') const albumId = event.url.searchParams.get('albumId')
// Build where clause // Build where clause
const whereConditions: any[] = [] const whereConditions: Prisma.MediaWhereInput[] = []
// Handle mime type filtering // Handle mime type filtering
if (mimeType && mimeType !== 'all') { if (mimeType && mimeType !== 'all') {
@ -149,7 +150,7 @@ export const GET: RequestHandler = async (event) => {
: {} : {}
// Build orderBy clause based on sort parameter // Build orderBy clause based on sort parameter
let orderBy: any = { createdAt: 'desc' } // default to newest let orderBy: Prisma.MediaOrderByWithRelationInput = { createdAt: 'desc' } // default to newest
switch (sort) { switch (sort) {
case 'oldest': case 'oldest':

View file

@ -128,7 +128,7 @@ export const DELETE: RequestHandler = async (event) => {
} }
// Check if media is in use // Check if media is in use
if (media.usedIn && (media.usedIn as any[]).length > 0) { if (media.usedIn && Array.isArray(media.usedIn) && media.usedIn.length > 0) {
return errorResponse('Cannot delete media that is in use', 409) return errorResponse('Cannot delete media that is in use', 409)
} }

View file

@ -51,7 +51,7 @@ async function extractExifData(file: File) {
if (!exif) return null if (!exif) return null
// Format EXIF data // Format EXIF data
const formattedExif: any = {} const formattedExif: ExifData = {}
// Camera info // Camera info
if (exif.Make && exif.Model) { if (exif.Make && exif.Model) {

View file

@ -1,4 +1,5 @@
import type { RequestHandler } from './$types' import type { RequestHandler } from './$types'
import type { Prisma } from '@prisma/client'
import { prisma } from '$lib/server/database' import { prisma } from '$lib/server/database'
import { uploadFile, isCloudinaryConfigured } from '$lib/server/cloudinary' import { uploadFile, isCloudinaryConfigured } from '$lib/server/cloudinary'
import { jsonResponse, errorResponse, checkAdminAuth } from '$lib/server/api-utils' import { jsonResponse, errorResponse, checkAdminAuth } from '$lib/server/api-utils'
@ -6,8 +7,27 @@ import { logger } from '$lib/server/logger'
import { dev } from '$app/environment' import { dev } from '$app/environment'
import exifr from 'exifr' import exifr from 'exifr'
// Type for formatted EXIF data
interface ExifData {
camera?: string
lens?: string
focalLength?: string
aperture?: string
shutterSpeed?: string
iso?: number
dateTaken?: string
gps?: {
latitude: number
longitude: number
altitude?: number
}
orientation?: number
colorSpace?: string
[key: string]: unknown
}
// Helper function to extract and format EXIF data // Helper function to extract and format EXIF data
async function extractExifData(file: File): Promise<any> { async function extractExifData(file: File): Promise<ExifData | null> {
try { try {
const buffer = await file.arrayBuffer() const buffer = await file.arrayBuffer()
const exif = await exifr.parse(buffer, { const exif = await exifr.parse(buffer, {
@ -33,7 +53,7 @@ async function extractExifData(file: File): Promise<any> {
if (!exif) return null if (!exif) return null
// Format the data into a more usable structure // Format the data into a more usable structure
const formattedExif: any = {} const formattedExif: ExifData = {}
if (exif.Make && exif.Model) { if (exif.Make && exif.Model) {
formattedExif.camera = `${exif.Make} ${exif.Model}`.trim() formattedExif.camera = `${exif.Make} ${exif.Model}`.trim()

View file

@ -1,9 +1,30 @@
import type { RequestHandler } from './$types' import type { RequestHandler } from './$types'
import type { Prisma } from '@prisma/client'
import { prisma } from '$lib/server/database' import { prisma } from '$lib/server/database'
import { jsonResponse, errorResponse } from '$lib/server/api-utils' import { jsonResponse, errorResponse } from '$lib/server/api-utils'
import { logger } from '$lib/server/logger' import { logger } from '$lib/server/logger'
import type { PhotoItem, PhotoAlbum, Photo } from '$lib/types/photos' import type { PhotoItem, PhotoAlbum, Photo } from '$lib/types/photos'
// Type for media with photo fields
interface PhotoMedia {
id: number
photoSlug: string | null
filename: string
url: string
thumbnailUrl: string | null
width: number | null
height: number | null
dominantColor: string | null
colors: Prisma.JsonValue
aspectRatio: number | null
photoCaption: string | null
photoTitle: string | null
photoDescription: string | null
createdAt: Date
photoPublishedAt: Date | null
exifData: Prisma.JsonValue
}
// GET /api/photos - Get individual photos only (albums excluded from collection) // GET /api/photos - Get individual photos only (albums excluded from collection)
export const GET: RequestHandler = async (event) => { export const GET: RequestHandler = async (event) => {
try { try {
@ -39,11 +60,11 @@ export const GET: RequestHandler = async (event) => {
}) })
// Helper function to extract date from EXIF data // Helper function to extract date from EXIF data
const getPhotoDate = (media: any): Date => { const getPhotoDate = (media: PhotoMedia): Date => {
// Try to get date from EXIF data // Try to get date from EXIF data
if (media.exifData && typeof media.exifData === 'object') { if (media.exifData && typeof media.exifData === 'object') {
// Check for common EXIF date fields // Check for common EXIF date fields
const exif = media.exifData as any const exif = media.exifData as Record<string, unknown>
const dateTaken = exif.DateTimeOriginal || exif.DateTime || exif.dateTaken const dateTaken = exif.DateTimeOriginal || exif.DateTime || exif.dateTaken
if (dateTaken) { if (dateTaken) {
// Parse EXIF date format (typically "YYYY:MM:DD HH:MM:SS") // Parse EXIF date format (typically "YYYY:MM:DD HH:MM:SS")

View file

@ -1,4 +1,5 @@
import type { RequestHandler } from './$types' import type { RequestHandler } from './$types'
import type { Prisma } from '@prisma/client'
import { prisma } from '$lib/server/database' import { prisma } from '$lib/server/database'
import { import {
jsonResponse, jsonResponse,
@ -29,7 +30,7 @@ export const GET: RequestHandler = async (event) => {
const postType = event.url.searchParams.get('postType') const postType = event.url.searchParams.get('postType')
// Build where clause // Build where clause
const where: any = {} const where: Prisma.PostWhereInput = {}
if (status) { if (status) {
where.status = status where.status = status
} }

View file

@ -1,4 +1,5 @@
import type { RequestHandler } from './$types' import type { RequestHandler } from './$types'
import type { Prisma } from '@prisma/client'
import { prisma } from '$lib/server/database' import { prisma } from '$lib/server/database'
import { jsonResponse, errorResponse, checkAdminAuth } from '$lib/server/api-utils' import { jsonResponse, errorResponse, checkAdminAuth } from '$lib/server/api-utils'
import { logger } from '$lib/server/logger' import { logger } from '$lib/server/logger'
@ -175,7 +176,7 @@ export const PATCH: RequestHandler = async (event) => {
} }
} }
const updateData: any = {} const updateData: Prisma.PostUpdateInput = {}
if (data.status !== undefined) { if (data.status !== undefined) {
updateData.status = data.status updateData.status = data.status