Update next-auth

This commit is contained in:
Sam Becker 2024-03-19 12:34:09 -05:00
parent e36f09185d
commit 91818ed15c
4 changed files with 111 additions and 79 deletions

View File

@ -40,7 +40,7 @@
"jest-environment-jsdom": "^29.7.0", "jest-environment-jsdom": "^29.7.0",
"nanoid": "^5.0.6", "nanoid": "^5.0.6",
"next": "14.1.3", "next": "14.1.3",
"next-auth": "5.0.0-beta.13", "next-auth": "5.0.0-beta.15",
"next-themes": "^0.3.0", "next-themes": "^0.3.0",
"postcss": "8.4.35", "postcss": "8.4.35",
"react": "18.2.0", "react": "18.2.0",

14
pnpm-lock.yaml generated
View File

@ -99,8 +99,8 @@ dependencies:
specifier: 14.1.3 specifier: 14.1.3
version: 14.1.3(@babel/core@7.23.9)(react-dom@18.2.0)(react@18.2.0) version: 14.1.3(@babel/core@7.23.9)(react-dom@18.2.0)(react@18.2.0)
next-auth: next-auth:
specifier: 5.0.0-beta.13 specifier: 5.0.0-beta.15
version: 5.0.0-beta.13(next@14.1.3)(react@18.2.0) version: 5.0.0-beta.15(next@14.1.3)(react@18.2.0)
next-themes: next-themes:
specifier: ^0.3.0 specifier: ^0.3.0
version: 0.3.0(react-dom@18.2.0)(react@18.2.0) version: 0.3.0(react-dom@18.2.0)(react@18.2.0)
@ -156,8 +156,8 @@ packages:
'@jridgewell/trace-mapping': 0.3.22 '@jridgewell/trace-mapping': 0.3.22
dev: false dev: false
/@auth/core@0.27.0: /@auth/core@0.28.0:
resolution: {integrity: sha512-3bydnRJIM/Al6mkYmb53MsC+6G8ojw3lLPzwgVnX4dCo6N2lrib6Wq6r0vxZIhuHGjLObqqtUfpeaEj5aeTHFg==} resolution: {integrity: sha512-/fh/tb/L4NMSYcyPoo4Imn8vN6MskcVfgESF8/ndgtI4fhD/7u7i5fTVzWgNRZ4ebIEGHNDbWFRxaTu1NtQgvA==}
peerDependencies: peerDependencies:
'@simplewebauthn/browser': ^9.0.1 '@simplewebauthn/browser': ^9.0.1
'@simplewebauthn/server': ^9.0.2 '@simplewebauthn/server': ^9.0.2
@ -6052,8 +6052,8 @@ packages:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
dev: false dev: false
/next-auth@5.0.0-beta.13(next@14.1.3)(react@18.2.0): /next-auth@5.0.0-beta.15(next@14.1.3)(react@18.2.0):
resolution: {integrity: sha512-2m2Gq69WQ0YXcHCCpHn2y5z1bxSlqD/XOuAgrdtz49/VIAdTFFeYZz97RYqf6xMF8VGmoG32VUnJ6LzaHk6Fwg==} resolution: {integrity: sha512-UQggNq8CDu3/w8CYkihKLLnRPNXel98K0j7mtjj9a6XTNYo4Hni8xg/2h1YhElW6vXE8mgtvmH11rU8NKw86jQ==}
peerDependencies: peerDependencies:
'@simplewebauthn/browser': ^9.0.1 '@simplewebauthn/browser': ^9.0.1
'@simplewebauthn/server': ^9.0.2 '@simplewebauthn/server': ^9.0.2
@ -6068,7 +6068,7 @@ packages:
nodemailer: nodemailer:
optional: true optional: true
dependencies: dependencies:
'@auth/core': 0.27.0 '@auth/core': 0.28.0
next: 14.1.3(@babel/core@7.23.9)(react-dom@18.2.0)(react@18.2.0) next: 14.1.3(@babel/core@7.23.9)(react-dom@18.2.0)(react@18.2.0)
react: 18.2.0 react: 18.2.0
dev: false dev: false

View File

@ -44,6 +44,17 @@ export const {
}, },
}); });
export const safelyRunServerAdminAction = async <T>(
callback: () => T,
): Promise<T> => {
const session = await auth();
if (session?.user) {
return callback();
} else {
throw new Error('Unauthorized server action request');
}
};
export const generateAuthSecret = () => fetch( export const generateAuthSecret = () => fetch(
'https://generate-secret.vercel.app/32', 'https://generate-secret.vercel.app/32',
{ cache: 'no-cache' }, { cache: 'no-cache' },

View File

@ -33,47 +33,54 @@ import {
import { extractExifDataFromBlobPath } from './server'; import { extractExifDataFromBlobPath } from './server';
import { TAG_FAVS, isTagFavs } from '@/tag'; import { TAG_FAVS, isTagFavs } from '@/tag';
import { convertPhotoToPhotoDbInsert } from '.'; import { convertPhotoToPhotoDbInsert } from '.';
import { safelyRunServerAdminAction } from '@/auth';
export async function createPhotoAction(formData: FormData) { export async function createPhotoAction(formData: FormData) {
const photo = convertFormDataToPhotoDbInsert(formData, true); return safelyRunServerAdminAction(async () => {
const photo = convertFormDataToPhotoDbInsert(formData, true);
const updatedUrl = await convertUploadToPhoto(photo.url, photo.id); const updatedUrl = await convertUploadToPhoto(photo.url, photo.id);
if (updatedUrl) { photo.url = updatedUrl; } if (updatedUrl) { photo.url = updatedUrl; }
await sqlInsertPhoto(photo); await sqlInsertPhoto(photo);
revalidateAllKeysAndPaths(); revalidateAllKeysAndPaths();
redirect(PATH_ADMIN_PHOTOS); redirect(PATH_ADMIN_PHOTOS);
});
} }
export async function updatePhotoAction(formData: FormData) { export async function updatePhotoAction(formData: FormData) {
const photo = convertFormDataToPhotoDbInsert(formData); return safelyRunServerAdminAction(async () => {
const photo = convertFormDataToPhotoDbInsert(formData);
await sqlUpdatePhoto(photo); await sqlUpdatePhoto(photo);
revalidateAllKeysAndPaths(); revalidateAllKeysAndPaths();
redirect(PATH_ADMIN_PHOTOS); redirect(PATH_ADMIN_PHOTOS);
});
} }
export async function toggleFavoritePhotoAction( export async function toggleFavoritePhotoAction(
photoId: string, photoId: string,
shouldRedirect?: boolean, shouldRedirect?: boolean,
) { ) {
const photo = await getPhoto(photoId); return safelyRunServerAdminAction(async () => {
if (photo) { const photo = await getPhoto(photoId);
const { tags } = photo; if (photo) {
photo.tags = tags.some(tag => tag === TAG_FAVS) const { tags } = photo;
? tags.filter(tag => !isTagFavs(tag)) photo.tags = tags.some(tag => tag === TAG_FAVS)
: [...tags, TAG_FAVS]; ? tags.filter(tag => !isTagFavs(tag))
await sqlUpdatePhoto(convertPhotoToPhotoDbInsert(photo)); : [...tags, TAG_FAVS];
revalidateAllKeysAndPaths(); await sqlUpdatePhoto(convertPhotoToPhotoDbInsert(photo));
if (shouldRedirect) { revalidateAllKeysAndPaths();
redirect(pathForPhoto(photoId)); if (shouldRedirect) {
redirect(pathForPhoto(photoId));
}
} }
} });
} }
export async function deletePhotoAction( export async function deletePhotoAction(
@ -81,82 +88,96 @@ export async function deletePhotoAction(
photoUrl: string, photoUrl: string,
shouldRedirect?: boolean, shouldRedirect?: boolean,
) { ) {
await sqlDeletePhoto(photoId).then(() => deleteStorageUrl(photoUrl)); return safelyRunServerAdminAction(async () => {
revalidateAllKeysAndPaths(); await sqlDeletePhoto(photoId).then(() => deleteStorageUrl(photoUrl));
if (shouldRedirect) { revalidateAllKeysAndPaths();
redirect(PATH_ROOT); if (shouldRedirect) {
} redirect(PATH_ROOT);
}
});
}; };
export async function deletePhotoFormAction(formData: FormData) { export async function deletePhotoFormAction(formData: FormData) {
return deletePhotoAction( return safelyRunServerAdminAction(async () =>
formData.get('id') as string, deletePhotoAction(
formData.get('url') as string, formData.get('id') as string,
formData.get('url') as string,
)
); );
}; };
export async function deletePhotoTagGloballyAction(formData: FormData) { export async function deletePhotoTagGloballyAction(formData: FormData) {
const tag = formData.get('tag') as string; return safelyRunServerAdminAction(async () => {
const tag = formData.get('tag') as string;
await sqlDeletePhotoTagGlobally(tag); await sqlDeletePhotoTagGlobally(tag);
revalidatePhotosKey(); revalidatePhotosKey();
revalidateAdminPaths(); revalidateAdminPaths();
});
} }
export async function renamePhotoTagGloballyAction(formData: FormData) { export async function renamePhotoTagGloballyAction(formData: FormData) {
const tag = formData.get('tag') as string; return safelyRunServerAdminAction(async () => {
const updatedTag = formData.get('updatedTag') as string; const tag = formData.get('tag') as string;
const updatedTag = formData.get('updatedTag') as string;
if (tag && updatedTag && tag !== updatedTag) { if (tag && updatedTag && tag !== updatedTag) {
await sqlRenamePhotoTagGlobally(tag, updatedTag); await sqlRenamePhotoTagGlobally(tag, updatedTag);
revalidatePhotosKey(); revalidatePhotosKey();
revalidateTagsKey(); revalidateTagsKey();
redirect(PATH_ADMIN_TAGS); redirect(PATH_ADMIN_TAGS);
} }
});
} }
export async function deleteBlobPhotoAction(formData: FormData) { export async function deleteBlobPhotoAction(formData: FormData) {
await deleteStorageUrl(formData.get('url') as string); return safelyRunServerAdminAction(async () => {
await deleteStorageUrl(formData.get('url') as string);
revalidateAdminPaths(); revalidateAdminPaths();
if (formData.get('redirectToPhotos') === 'true') { if (formData.get('redirectToPhotos') === 'true') {
redirect(PATH_ADMIN_PHOTOS); redirect(PATH_ADMIN_PHOTOS);
} }
});
} }
export async function getExifDataAction( export async function getExifDataAction(
photoFormPrevious: Partial<PhotoFormData>, photoFormPrevious: Partial<PhotoFormData>,
): Promise<Partial<PhotoFormData>> { ): Promise<Partial<PhotoFormData>> {
const { url } = photoFormPrevious; return safelyRunServerAdminAction(async () => {
if (url) { const { url } = photoFormPrevious;
const { photoFormExif } = await extractExifDataFromBlobPath(url); if (url) {
if (photoFormExif) { const { photoFormExif } = await extractExifDataFromBlobPath(url);
return photoFormExif; if (photoFormExif) {
return photoFormExif;
}
} }
} return {};
return {}; });
} }
export async function syncPhotoExifDataAction(formData: FormData) { export async function syncPhotoExifDataAction(formData: FormData) {
const photoId = formData.get('id') as string; return safelyRunServerAdminAction(async () => {
if (photoId) { const photoId = formData.get('id') as string;
const photo = await getPhoto(photoId); if (photoId) {
if (photo) { const photo = await getPhoto(photoId);
const { photoFormExif } = await extractExifDataFromBlobPath(photo.url); if (photo) {
if (photoFormExif) { const { photoFormExif } = await extractExifDataFromBlobPath(photo.url);
const photoFormDbInsert = convertFormDataToPhotoDbInsert({ if (photoFormExif) {
...convertPhotoToFormData(photo), const photoFormDbInsert = convertFormDataToPhotoDbInsert({
...photoFormExif, ...convertPhotoToFormData(photo),
}); ...photoFormExif,
await sqlUpdatePhoto(photoFormDbInsert); });
revalidatePhotosKey(); await sqlUpdatePhoto(photoFormDbInsert);
revalidatePhotosKey();
}
} }
} }
} });
} }
export async function syncCacheAction() { export async function syncCacheAction() {
revalidateAllKeysAndPaths(); return safelyRunServerAdminAction(revalidateAllKeysAndPaths);
} }