diff --git a/src/auth/index.ts b/src/auth/index.ts index 4ea60774..20ae70af 100644 --- a/src/auth/index.ts +++ b/src/auth/index.ts @@ -44,7 +44,7 @@ export const { }, }); -export const safelyRunServerAdminAction = async ( +export const safelyRunAdminServerAction = async ( callback: () => T, ): Promise => { const session = await auth(); diff --git a/src/photo/actions.ts b/src/photo/actions.ts index 7b2033e7..759e2641 100644 --- a/src/photo/actions.ts +++ b/src/photo/actions.ts @@ -33,10 +33,11 @@ import { import { extractExifDataFromBlobPath } from './server'; import { TAG_FAVS, isTagFavs } from '@/tag'; import { convertPhotoToPhotoDbInsert } from '.'; -import { safelyRunServerAdminAction } from '@/auth'; +import { safelyRunAdminServerAction } from '@/auth'; +import { ImageQuery, streamImageQuery } from './ai'; export async function createPhotoAction(formData: FormData) { - return safelyRunServerAdminAction(async () => { + return safelyRunAdminServerAction(async () => { const photo = convertFormDataToPhotoDbInsert(formData, true); const updatedUrl = await convertUploadToPhoto(photo.url, photo.id); @@ -52,7 +53,7 @@ export async function createPhotoAction(formData: FormData) { } export async function updatePhotoAction(formData: FormData) { - return safelyRunServerAdminAction(async () => { + return safelyRunAdminServerAction(async () => { const photo = convertFormDataToPhotoDbInsert(formData); await sqlUpdatePhoto(photo); @@ -67,7 +68,7 @@ export async function toggleFavoritePhotoAction( photoId: string, shouldRedirect?: boolean, ) { - return safelyRunServerAdminAction(async () => { + return safelyRunAdminServerAction(async () => { const photo = await getPhoto(photoId); if (photo) { const { tags } = photo; @@ -88,7 +89,7 @@ export async function deletePhotoAction( photoUrl: string, shouldRedirect?: boolean, ) { - return safelyRunServerAdminAction(async () => { + return safelyRunAdminServerAction(async () => { await sqlDeletePhoto(photoId).then(() => deleteStorageUrl(photoUrl)); revalidateAllKeysAndPaths(); if (shouldRedirect) { @@ -98,7 +99,7 @@ export async function deletePhotoAction( }; export async function deletePhotoFormAction(formData: FormData) { - return safelyRunServerAdminAction(async () => + return safelyRunAdminServerAction(async () => deletePhotoAction( formData.get('id') as string, formData.get('url') as string, @@ -107,7 +108,7 @@ export async function deletePhotoFormAction(formData: FormData) { }; export async function deletePhotoTagGloballyAction(formData: FormData) { - return safelyRunServerAdminAction(async () => { + return safelyRunAdminServerAction(async () => { const tag = formData.get('tag') as string; await sqlDeletePhotoTagGlobally(tag); @@ -118,7 +119,7 @@ export async function deletePhotoTagGloballyAction(formData: FormData) { } export async function renamePhotoTagGloballyAction(formData: FormData) { - return safelyRunServerAdminAction(async () => { + return safelyRunAdminServerAction(async () => { const tag = formData.get('tag') as string; const updatedTag = formData.get('updatedTag') as string; @@ -132,7 +133,7 @@ export async function renamePhotoTagGloballyAction(formData: FormData) { } export async function deleteBlobPhotoAction(formData: FormData) { - return safelyRunServerAdminAction(async () => { + return safelyRunAdminServerAction(async () => { await deleteStorageUrl(formData.get('url') as string); revalidateAdminPaths(); @@ -146,7 +147,7 @@ export async function deleteBlobPhotoAction(formData: FormData) { export async function getExifDataAction( photoFormPrevious: Partial, ): Promise> { - return safelyRunServerAdminAction(async () => { + return safelyRunAdminServerAction(async () => { const { url } = photoFormPrevious; if (url) { const { photoFormExif } = await extractExifDataFromBlobPath(url); @@ -159,7 +160,7 @@ export async function getExifDataAction( } export async function syncPhotoExifDataAction(formData: FormData) { - return safelyRunServerAdminAction(async () => { + return safelyRunAdminServerAction(async () => { const photoId = formData.get('id') as string; if (photoId) { const photo = await getPhoto(photoId); @@ -179,5 +180,13 @@ export async function syncPhotoExifDataAction(formData: FormData) { } export async function syncCacheAction() { - return safelyRunServerAdminAction(revalidateAllKeysAndPaths); + return safelyRunAdminServerAction(revalidateAllKeysAndPaths); +} + +export async function streamImageQueryAction( + imageBase64: string, + query: ImageQuery, +) { + return safelyRunAdminServerAction(async () => + streamImageQuery(imageBase64, query)); } diff --git a/src/photo/ai.ts b/src/photo/ai.ts new file mode 100644 index 00000000..6e074de3 --- /dev/null +++ b/src/photo/ai.ts @@ -0,0 +1,13 @@ +import { streamOpenAiImageQuery } from '@/services/openai'; + +export type ImageQuery = 'title' | 'caption' | 'tags' | 'description'; + +export const IMAGE_QUERIES: Record = { + title: 'What is the title of this image?', + caption: 'What is the caption of this image?', + tags: 'Describe this image three or less comma-separated keywords', + description: 'Describe this image in detail', +}; + +export const streamImageQuery = (imageBase64: string, query: ImageQuery) => + streamOpenAiImageQuery(imageBase64, IMAGE_QUERIES[query]); diff --git a/src/services/openai.ts b/src/services/openai.ts index 0d91a18b..602d5a02 100644 --- a/src/services/openai.ts +++ b/src/services/openai.ts @@ -5,7 +5,10 @@ import { createStreamableValue, render } from 'ai/rsc'; const provider = new OpenAI({ apiKey: process.env.OPENAI_SECRET_KEY }); -const streamImageQueryRaw = async (imageBase64: string, query: string) => { +export const streamOpenAiImageQuery = async ( + imageBase64: string, + query: string, +) => { const stream = createStreamableValue(''); render({ @@ -36,15 +39,3 @@ const streamImageQueryRaw = async (imageBase64: string, query: string) => { return stream.value; }; - -export type ImageQuery = 'title' | 'caption' | 'tags' | 'description'; - -export const IMAGE_QUERIES: Record = { - title: 'What is the title of this image?', - caption: 'What is the caption of this image?', - tags: 'Describe this image three or less comma-separated keywords', - description: 'Describe this image in detail', -}; - -export const streamImageQuery = (imageBase64: string, query: ImageQuery) => - streamImageQueryRaw(imageBase64, IMAGE_QUERIES[query]);