From fbb24185dfbcee1134cc14c80f8254b11782c2be Mon Sep 17 00:00:00 2001 From: Gib Date: Tue, 24 Jun 2025 15:56:44 -0500 Subject: [PATCH] Making progress on rewrite. Recreating queries and hooks now. --- bun.lockb | Bin 357084 -> 367596 bytes package.json | 11 +- src/app/layout.tsx | 235 ++++++++++++++++++++++++++-- src/lib/hooks/context/index.tsx | 3 +- src/lib/hooks/context/use-auth.tsx | 143 ++++++++++++++++- src/lib/hooks/context/use-query.tsx | 72 ++++++++- src/lib/hooks/index.ts | 1 + src/lib/hooks/use-file-upload.ts | 82 ++++++++++ src/lib/queries/auth.ts | 118 ++++++++++++++ src/lib/queries/index.ts | 22 +++ src/lib/queries/storage.ts | 178 +++++++++++++++++++++ src/utils/supabase/client.ts | 5 +- src/utils/supabase/server.ts | 4 +- 13 files changed, 840 insertions(+), 34 deletions(-) create mode 100644 src/lib/hooks/index.ts create mode 100644 src/lib/hooks/use-file-upload.ts create mode 100644 src/lib/queries/auth.ts create mode 100644 src/lib/queries/index.ts create mode 100644 src/lib/queries/storage.ts diff --git a/bun.lockb b/bun.lockb index e8fba36a8aad734c2a95366bfbfdb2a52c001c33..f87b3602e7622e4e263365012977bb9cfcdc5cd7 100755 GIT binary patch delta 72399 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 TcJ1frX;&?wBn9@>&;9=ZI}*%` delta 66279 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 tCpskl?Qdb}K+$d%mWjn0S`=ww9g5#bd diff --git a/package.json b/package.json index 9826284..9e76725 100644 --- a/package.json +++ b/package.json @@ -48,11 +48,12 @@ "@radix-ui/react-toggle": "^1.1.9", "@radix-ui/react-toggle-group": "^1.1.10", "@radix-ui/react-tooltip": "^1.2.7", - "@sentry/nextjs": "^9.30.0", + "@sentry/nextjs": "^9.31.0", + "@supabase-cache-helpers/postgrest-react-query": "^1.13.4", "@supabase/ssr": "^0.6.1", - "@supabase/supabase-js": "^2.50.0", + "@supabase/supabase-js": "^2.50.1", "@t3-oss/env-nextjs": "^0.12.0", - "@tanstack/react-query": "^5.80.10", + "@tanstack/react-query": "^5.81.2", "@tanstack/react-table": "^8.21.3", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", @@ -93,12 +94,12 @@ "eslint-plugin-drizzle": "^0.2.3", "eslint-plugin-prettier": "^5.5.0", "postcss": "^8.5.6", - "prettier": "^3.5.3", + "prettier": "^3.6.0", "prettier-plugin-tailwindcss": "^0.6.13", "tailwindcss": "^4.1.10", "tw-animate-css": "^1.3.4", "typescript": "^5.8.3", - "typescript-eslint": "^8.34.1" + "typescript-eslint": "^8.35.0" }, "ct3aMetadata": { "initVersion": "7.39.3" diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 2c36ac1..3542f7b 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,15 +1,207 @@ import '@/styles/globals.css'; - import { type Metadata } from 'next'; import { Geist } from 'next/font/google'; import { - ReactQueryClientProvider, + AuthContextProvider, + ThemeProvider, + TVModeProvider, + QueryClientProvider, } from '@/lib/hooks/context'; +import PlausibleProvider from 'next-plausible'; +import { Toaster } from '@/components/ui'; +import * as Sentry from '@sentry/nextjs'; -export const metadata: Metadata = { - title: 'Create T3 App', - description: 'Generated by create-t3-app', - icons: [{ rel: 'icon', url: '/favicon.ico' }], +export const generateMetadata = (): Metadata => { + return { + title: { + template: '%s | Next Template', + default: 'Next Template', + }, + description: 'Gib\'s Next Template', + applicationName: 'Next Template', + keywords: 'Next.js, Supabase, Tailwind, Tanstack, React, Query, T3, Gib', + authors: [{ name: 'Gib', url: 'https://gbrown.org' }], + creator: 'Gib Brown', + publisher: 'Gib Brown', + other: { + ...Sentry.getTraceData(), + }, + formatDetection: { + email: false, + address: false, + telephone: false, + }, + robots: { + index: true, + follow: true, + nocache: false, + googleBot: { + index: true, + follow: true, + noimageindex: false, + 'max-video-preview': -1, + 'max-image-preview': 'large', + 'max-snippet': -1, + }, + }, + icons: { + icon: [ + { url: '/favicon.ico', type: 'image/x-icon', sizes: 'any' }, + { url: '/favicon-16.png', type: 'image/png', sizes: '16x16' }, + { url: '/favicon-32.png', type: 'image/png', sizes: '32x32' }, + { url: '/favicon.png', type: 'image/png', sizes: '96x96' }, + { url: '/appicon/icon-36.png', type: 'image/png', sizes: '36x36' }, + { url: '/appicon/icon-48.png', type: 'image/png', sizes: '48x48' }, + { url: '/appicon/icon-72.png', type: 'image/png', sizes: '72x72' }, + { url: '/appicon/icon-96.png', type: 'image/png', sizes: '96x96' }, + { url: '/appicon/icon-144.png', type: 'image/png', sizes: '144x144' }, + { url: '/appicon/icon.png', type: 'image/png', sizes: '192x192' }, + /* + { + url: '/favicon.ico', type: 'image/x-icon', + sizes: 'any', media: '(prefers-color-scheme: dark)' + }, + { + url: '/favicon-16.png', type: 'image/png', + sizes: '16x16', media: '(prefers-color-scheme: dark)' + }, + { + url: '/favicon-32.png', type: 'image/png', + sizes: '32x32', media: '(prefers-color-scheme: dark)' + }, + { + url: '/favicon.png', type: 'image/png', + sizes: '96x96', media: '(prefers-color-scheme: dark)' + }, + { + url: '/appicon/icon-36.png', type: 'image/png', + sizes: '36x36', media: '(prefers-color-scheme: dark)' + }, + { + url: '/appicon/icon-48.png', type: 'image/png', + sizes: '48x48', media: '(prefers-color-scheme: dark)' + }, + { + url: '/appicon/icon-72.png', type: 'image/png', + sizes: '72x72', media: '(prefers-color-scheme: dark)' + }, + { + url: '/appicon/icon-96.png', type: 'image/png', + sizes: '96x96', media: '(prefers-color-scheme: dark)' + }, + { + url: '/appicon/icon-144.png', type: 'image/png', + sizes: '144x144', media: '(prefers-color-scheme: dark)' + }, + { + url: '/appicon/icon.png', type: 'image/png', + sizes: '192x192', media: '(prefers-color-scheme: dark)' + }, + */ + ], + apple: [ + { url: '/appicon/icon-36.png', type: 'image/png', sizes: '36x36' }, + { url: '/appicon/icon-48.png', type: 'image/png', sizes: '48x48' }, + { url: '/appicon/icon-72.png', type: 'image/png', sizes: '72x72' }, + { url: '/appicon/icon-96.png', type: 'image/png', sizes: '96x96' }, + { url: '/appicon/icon-144.png', type: 'image/png', sizes: '144x144' }, + { url: '/appicon/icon.png', type: 'image/png', sizes: '192x192' }, + /* + { + url: '/appicon/icon-36.png', type: 'image/png', + sizes: '36x36', media: '(prefers-color-scheme: dark)' + }, + { + url: '/appicon/icon-48.png', type: 'image/png', + sizes: '48x48', media: '(prefers-color-scheme: dark)' + }, + { + url: '/appicon/icon-72.png', type: 'image/png', + sizes: '72x72', media: '(prefers-color-scheme: dark)' + }, + { + url: '/appicon/icon-96.png', type: 'image/png', + sizes: '96x96', media: '(prefers-color-scheme: dark)' + }, + { + url: '/appicon/icon-144.png', type: 'image/png', + sizes: '144x144', media: '(prefers-color-scheme: dark)' + }, + { + url: '/appicon/icon.png', type: 'image/png', + sizes: '192x192', media: '(prefers-color-scheme: dark)' + }, + */ + ], + shortcut: [ + { url: '/appicon/icon-36.png', type: 'image/png', sizes: '36x36' }, + { url: '/appicon/icon-48.png', type: 'image/png', sizes: '48x48' }, + { url: '/appicon/icon-72.png', type: 'image/png', sizes: '72x72' }, + { url: '/appicon/icon-96.png', type: 'image/png', sizes: '96x96' }, + { url: '/appicon/icon-144.png', type: 'image/png', sizes: '144x144' }, + { url: '/appicon/icon.png', type: 'image/png', sizes: '192x192' }, + /* + { + url: '/appicon/icon-36.png', type: 'image/png', + sizes: '36x36', media: '(prefers-color-scheme: dark)' + }, + { + url: '/appicon/icon-48.png', type: 'image/png', + sizes: '48x48', media: '(prefers-color-scheme: dark)' + }, + { + url: '/appicon/icon-72.png', type: 'image/png', + sizes: '72x72', media: '(prefers-color-scheme: dark)' + }, + { + url: '/appicon/icon-96.png', type: 'image/png', + sizes: '96x96', media: '(prefers-color-scheme: dark)' + }, + { + url: '/appicon/icon-144.png', type: 'image/png', + sizes: '144x144', media: '(prefers-color-scheme: dark)' + }, + { + url: '/appicon/icon.png', type: 'image/png', + sizes: '192x192', media: '(prefers-color-scheme: dark)' + }, + */ + ], + }, + /* + appleWebApp: { + title: 'Tech Tracker', + statusBarStyle: 'black-translucent', + startupImage: [ + '/appicon/apple/splash-768x1024.png', + { + url: '/appicon/apple/splash-1536x2008.png', + media: '(device-width: 768px) and (device-height: 1024px)' + }, + ], + }, + verification: { + google: 'google', + yandex: 'yandex', + yahoo: 'yahoo', + }, + category: 'technology', + appLinks: { + ios: { + url: 'https://git.gbrown.org/next-template', + app_store_id: 'org.gbrown.next-template', + }, + android: { + package: 'https://git.gbrown.org/next-template', + app_name: 'app_t3_template', + }, + web: { + url: 'https://git.gbrown.org/next-template', + should_fallback: true, + }, + }, + */ + }; }; const geist = Geist({ @@ -21,10 +213,31 @@ export default function RootLayout({ children, }: Readonly<{ children: React.ReactNode }>) { return ( - - - {children} - - + + + + + + + + {children} + + + + + + + + ); } diff --git a/src/lib/hooks/context/index.tsx b/src/lib/hooks/context/index.tsx index 080964a..474d9dc 100644 --- a/src/lib/hooks/context/index.tsx +++ b/src/lib/hooks/context/index.tsx @@ -1,4 +1,5 @@ +export { AuthContextProvider, useAuth } from './use-auth'; export { useIsMobile } from './use-mobile'; -export { ReactQueryClientProvider } from './use-query'; +export { QueryClientProvider, QueryErrorCodes } from './use-query'; export { ThemeProvider, ThemeToggle } from './use-theme'; export { TVModeProvider, useTVMode, TVToggle } from './use-tv-mode'; diff --git a/src/lib/hooks/context/use-auth.tsx b/src/lib/hooks/context/use-auth.tsx index 32e7362..6c76b24 100644 --- a/src/lib/hooks/context/use-auth.tsx +++ b/src/lib/hooks/context/use-auth.tsx @@ -1,11 +1,146 @@ 'use client'; - import React, { type ReactNode, createContext, - useCallback, useContext, useEffect, - useRef, - useState, } from 'react'; +import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; +import { useQuery as useSupabaseQuery } from '@supabase-cache-helpers/postgrest-react-query'; +import { QueryErrorCodes } from '@/lib/hooks/context'; +import { type User, type Profile, useSupabaseClient } from '@/utils/supabase'; +import { toast } from 'sonner'; +import { + getAvatar, + getCurrentUser, + getProfile, + updateProfile as updateProfileQuery +} from '@/lib/queries'; + +type AuthContextType = { + user: User | null; + profile: Profile | null; + avatar: string | null; + loading: boolean; + isAuthenticated: boolean; + updateProfile: (data: { + full_name?: string; + email?: string; + avatar_url?: string; + }) => Promise<{ data?: Profile; error?: unknown }>; + refreshUser: () => Promise; +}; + +const AuthContext = createContext(undefined); + +const AuthContextProvider = ({ children }: { children: ReactNode }) => { + const queryClient = useQueryClient(); + const supabase = useSupabaseClient(); + + if (!supabase) throw new Error('Supabase client not found!'); + + // User query + const { + data: userData, + isLoading: userLoading, + error: userError, + } = useQuery({ + queryKey: ['auth', 'user'], + queryFn: async () => { + const result = await getCurrentUser(supabase); + if (result.error) throw result.error; + return result.data.user as User | null; + }, + retry: false, + meta: { errCode: QueryErrorCodes.FETCH_USER_FAILED }, + }); + + // Profile query + const { + data: profileData, + isLoading: profileLoading, + } = useSupabaseQuery( + getProfile(supabase, userData?.id ?? ''), + { + enabled: !!userData?.id, + meta: { errCode: QueryErrorCodes.FETCH_PROFILE_FAILED }, + } + ); + + // Avatar query + const { + data: avatarData, + } = useQuery({ + queryKey: ['auth', 'avatar', profileData?.avatar_url], + queryFn: async () => { + if (!profileData?.avatar_url) return null; + const result = await getAvatar(supabase, profileData.avatar_url); + if (result.error) throw result.error; + return result.data.signedUrl as string | null; + }, + enabled: !!profileData?.avatar_url, + meta: { errCode: QueryErrorCodes.FETCH_AVATAR_FAILED }, + }); + + // Update profile mutation + const updateProfileMutation = useMutation({ + mutationFn: async (updates: Partial) => { + if (!userData?.id) throw new Error('User ID is required!'); + const result = await updateProfileQuery(supabase, userData.id, updates); + if (result.error) throw result.error; + return result.data; + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['auth'] }) + .catch((error) => console.error('Error invalidating auth queries:', error)); + toast.success('Profile updated successfully!'); + }, + meta: { errCode: QueryErrorCodes.UPDATE_PROFILE_FAILED }, + }); + + useEffect(() => { + const { + data: { subscription }, + } = supabase.auth.onAuthStateChange(async (event, _session) => { + if (event === 'SIGNED_IN' || event === 'SIGNED_OUT' || event === 'TOKEN_REFRESHED') { + await queryClient.invalidateQueries({ queryKey: ['auth'] }); + } + }); + return () => subscription.unsubscribe(); + }, [supabase.auth, queryClient]); + + const handleUpdateProfile = async (data: Partial) => { + try { + const result = await updateProfileMutation.mutateAsync(data); + return { data: result }; + } catch (error) { + return { error }; + } + }; + + const refreshUser = async () => { + await queryClient.invalidateQueries({ queryKey: ['auth'] }); + }; + + const value: AuthContextType = { + user: userData ?? null, + profile: profileData ?? null, + avatar: avatarData ?? null, + loading: userLoading || profileLoading, + isAuthenticated: !!userData && !userError, + updateProfile: handleUpdateProfile, + refreshUser, + }; + + return {children}; +}; + +const useAuth = () => { + const context = useContext(AuthContext); + if (!context || context === undefined) { + throw new Error('useAuth must be used within an AuthContextProvider'); + } + return context; +}; + +export { AuthContextProvider, useAuth }; diff --git a/src/lib/hooks/context/use-query.tsx b/src/lib/hooks/context/use-query.tsx index 94659a4..ae75796 100644 --- a/src/lib/hooks/context/use-query.tsx +++ b/src/lib/hooks/context/use-query.tsx @@ -1,22 +1,78 @@ -'use client' +'use client'; +import { + QueryClient, + QueryClientProvider as ReactQueryClientProvider, + QueryCache, + MutationCache, +} from '@tanstack/react-query'; +import { useState } from 'react'; +import { toast } from 'sonner'; -import { QueryClient, QueryClientProvider } from '@tanstack/react-query' -import { useState } from 'react' +const enum QueryErrorCodes { + FETCH_USER_FAILED = 'FETCH_USER_FAILED', + FETCH_PROFILE_FAILED = 'FETCH_PROFILE_FAILED', + FETCH_AVATAR_FAILED = 'FETCH_AVATAR_FAILED', + UPDATE_PROFILE_FAILED = 'UPDATE_PROFILE_FAILED', + UPLOAD_PHOTO_FAILED = 'UPLOAD_PHOTO_FAILED', +}; -const ReactQueryClientProvider = ({ children }: { children: React.ReactNode }) => { +const queryCacheOnError = (error: unknown, query: any) => { + const errorMessage = error instanceof Error ? error.message : error as string; + switch (query.meta?.errCode) { + case QueryErrorCodes.FETCH_USER_FAILED: + toast.error('Failed to fetch user!'); + break; + case QueryErrorCodes.FETCH_PROFILE_FAILED: + toast.error('Failed to fetch profile!'); + break; + case QueryErrorCodes.FETCH_AVATAR_FAILED: + console.warn('Failed to fetch avatar. User may not have one!') + break; + default: + console.error('Query error:', error); + break; + } +}; + +const mutationCacheOnError = ( + error: unknown, + variables: unknown, + context: unknown, + mutation: any, +) => { + const errorMessage = error instanceof Error ? error.message : error as string; + switch (mutation.meta?.errCode) { + case QueryErrorCodes.UPDATE_PROFILE_FAILED: + toast.error(`Failed to update user profile: ${errorMessage}`) + break; + case QueryErrorCodes.UPLOAD_PHOTO_FAILED: + toast.error(`Failed to upload photo: ${errorMessage}`) + break; + default: + console.error('Mutation error:', error); + break; + } +}; + + +const QueryClientProvider = ({ children }: { children: React.ReactNode }) => { const [queryClient] = useState( () => new QueryClient({ + queryCache: new QueryCache({ + onError: queryCacheOnError, + }), + mutationCache: new MutationCache({ + onError: mutationCacheOnError, + }), defaultOptions: { queries: { - // With SSR, we usually want to set some default staleTime - // above 0 to avoid refetching immediately on the client staleTime: 60 * 1000, }, }, }) ) - return {children} + return {children} }; -export { ReactQueryClientProvider }; +export { QueryClientProvider, QueryErrorCodes }; diff --git a/src/lib/hooks/index.ts b/src/lib/hooks/index.ts new file mode 100644 index 0000000..0ce7e35 --- /dev/null +++ b/src/lib/hooks/index.ts @@ -0,0 +1 @@ +export { useFileUpload } from './use-file-upload'; diff --git a/src/lib/hooks/use-file-upload.ts b/src/lib/hooks/use-file-upload.ts new file mode 100644 index 0000000..69a6aa0 --- /dev/null +++ b/src/lib/hooks/use-file-upload.ts @@ -0,0 +1,82 @@ +'use client'; +import { useState, useRef } from 'react'; +import { uploadFile, resizeImage } from '@/lib/queries'; +import { toast } from 'sonner'; +import { useAuth } from '@/lib/hooks/context'; +import { type SupabaseClient } from '@/utils/supabase'; + +type UploadToStorageProps = { + client: SupabaseClient; + file: File; + bucket: string; + resize?: false | { + maxWidth?: number; + maxHeight?: number; + quality?: number; + }, + replace?: false | string, +}; + +const useFileUpload = () => { + const [isUploading, setIsUploading] = useState(false); + const fileInputRef = useRef(null); + const { profile, isAuthenticated } = useAuth(); + + const uploadToStorage = async ({ + client, + file, + bucket, + resize = false, + replace = false, + }: UploadToStorageProps) => { + try { + if (!isAuthenticated) + throw new Error('User is not authenticated!'); + setIsUploading(true); + let fileToUpload = file; + if (resize && file.type.startsWith('image/')) + fileToUpload = await resizeImage({file, options: resize}); + if (replace) { + const { data, error} = await uploadFile({ + client, + bucket, + path: replace, + file: fileToUpload, + options: { + contentType: file.type, + upsert: true, + }, + }); + if (error) throw error; + return data + } else { + const fileExt = file.name.split('.').pop(); + const fileName = `${Date.now()}-${profile?.id}.${fileExt}`; + const { data, error } = await uploadFile({ + client, + bucket, + path: fileName, + file: fileToUpload, + options: { + contentType: file.type, + }, + }); + if (error) throw error; + return data; + } + } catch (error) { + toast.error(`Error uploading file: ${error as string}`); + return error; + } finally { + setIsUploading(false); + if (fileInputRef.current) fileInputRef.current.value = ''; + } + }; + return { + isUploading, + fileInputRef, + uploadToStorage, + }; +}; + +export { useFileUpload }; diff --git a/src/lib/queries/auth.ts b/src/lib/queries/auth.ts new file mode 100644 index 0000000..f714c83 --- /dev/null +++ b/src/lib/queries/auth.ts @@ -0,0 +1,118 @@ +import { type SupabaseClient, type Profile } from '@/utils/supabase'; +import { getSignedUrl } from '@/lib/queries'; + +const signUp = (client: SupabaseClient, formData: FormData) => { + const full_name = formData.get('name') as string; + const email = formData.get('email') as string; + const password = formData.get('password') as string; + const origin = process.env.NEXT_PUBLIC_SITE_URL!; + return client.auth.signUp({ + email, + password, + options: { + emailRedirectTo: `${origin}/auth/callback`, + data: { + full_name, + email, + provider: 'email', + } + } + }); +}; + +const signIn = (client: SupabaseClient, formData: FormData) => { + const email = formData.get('email') as string; + const password = formData.get('password') as string; + return client.auth.signInWithPassword({ email, password }); +}; + +const signInWithMicrosoft = (client: SupabaseClient) => { + const origin = process.env.NEXT_PUBLIC_SITE_URL!; + return client.auth.signInWithOAuth({ + provider: 'azure', + options: { + scopes: 'openid profile email offline_access', + redirectTo: `${origin}/auth/callback?redirect_to=/auth/success`, + }, + }); +}; + +const signInWithApple = (client: SupabaseClient) => { + const origin = process.env.NEXT_PUBLIC_SITE_URL!; + return client.auth.signInWithOAuth({ + provider: 'apple', + options: { + scopes: 'openid profile email offline_access', + redirectTo: `${origin}/auth/callback?redirect_to=/auth/success`, + }, + }); +}; + +const forgotPassword = (client: SupabaseClient, formData: FormData) => { + const email = formData.get('email') as string; + const origin = process.env.NEXT_PUBLIC_SITE_URL!; + return client.auth.resetPasswordForEmail(email, { + redirectTo: `${origin}/auth/callback?redirect_to=/profile`, + }); +}; + +const resetPassword = (client: SupabaseClient, formData: FormData) => { + const password = formData.get('password') as string; + return client.auth.updateUser({ password }); +}; + +const signOut = (client: SupabaseClient) => { + return client.auth.signOut(); +} + +const getCurrentUser = (client: SupabaseClient) => { + return client.auth.getUser(); +}; + +const getProfile = (client: SupabaseClient, userId: string) => { + return client + .from(`profiles`) + .select(`*`) + .eq(`id`, userId) + .single(); +}; + +const getAvatar = (client: SupabaseClient, avatarUrl: string) => { + return getSignedUrl({ + client, + bucket: 'avatars', + path: avatarUrl, + seconds: 3600, + transform: { + width: 128, + height: 128, + }, + }); +}; + +const updateProfile = ( + client: SupabaseClient, + userId: string, + updates: Partial, +) => { + return client + .from(`profiles`) + .update(updates) + .eq(`id`, userId) + .select() + .single(); +}; + +export { + forgotPassword, + getCurrentUser, + getProfile, + getAvatar, + resetPassword, + signIn, + signInWithApple, + signInWithMicrosoft, + signOut, + signUp, + updateProfile +}; diff --git a/src/lib/queries/index.ts b/src/lib/queries/index.ts new file mode 100644 index 0000000..9d95d0f --- /dev/null +++ b/src/lib/queries/index.ts @@ -0,0 +1,22 @@ +export { + forgotPassword, + getCurrentUser, + getProfile, + getAvatar, + resetPassword, + signIn, + signInWithApple, + signInWithMicrosoft, + signOut, + signUp, + updateProfile +} from './auth'; +export { + deleteFiles, + getPublicUrl, + getSignedUrl, + listFiles, + resizeImage, + uploadFile, + updateFile +} from './storage'; diff --git a/src/lib/queries/storage.ts b/src/lib/queries/storage.ts new file mode 100644 index 0000000..96b36fc --- /dev/null +++ b/src/lib/queries/storage.ts @@ -0,0 +1,178 @@ +import { type SupabaseClient, type Profile } from '@/utils/supabase'; + +type GetStorageProps = { + client: SupabaseClient; + bucket: string; + path: string; + seconds?: number; + transform?: { + width?: number; + height?: number; + quality?: number; + format?: 'origin'; + resize?: 'cover' | 'contain' | 'fill'; + }; + download?: boolean | string; +}; + +type UploadStorageProps = { + client: SupabaseClient; + bucket: string; + path: string; + file: File; + options?: { + cacheControl?: string; + contentType?: string; + upsert?: boolean; + }; +}; + +type ResizeImageProps = { + file: File; + options?: { + maxWidth?: number; + maxHeight?: number; + quality?: number; + }; +}; + +const getPublicUrl = ({ + client, + bucket, + path, + transform = {}, + download = false, +}: GetStorageProps) => { + return client.storage + .from(bucket) + .getPublicUrl(path, { download, transform}); +}; + +const getSignedUrl = ({ + client, + bucket, + path, + seconds = 3600, + transform = {}, + download = false, +}: GetStorageProps) => { + return client.storage + .from(bucket) + .createSignedUrl(path, seconds, { download, transform}); +}; + +const uploadFile = ({ + client, + bucket, + path, + file, + options = {}, +}: UploadStorageProps) => { + return client.storage + .from(bucket) + .upload(path, file, options); +}; + +const updateFile = ({ + client, + bucket, + path, + file, + options = { + upsert: true, + }, +}: UploadStorageProps) => { + return client.storage + .from(bucket) + .update(path, file, options); +}; + +const deleteFiles = ({ + client, + bucket, + path, +}: { + client: SupabaseClient; + bucket: string; + path: string[]; + }) => { + return client.storage.from(bucket).remove(path); +}; + +const listFiles = ({ + client, + bucket, + path = '', + options = {}, +}: { + client: SupabaseClient; + bucket: string; + path?: string; + options?: { + limit?: number; + offset?: number; + sortBy?: { column: string, order: 'asc' | 'desc' }; + }; +}) => { + return client.storage.from(bucket).list(path, options); +}; + +const resizeImage = async ({ + file, + options = { + maxWidth: 800, + maxHeight: 800, + quality: 0.8, + }, +}: ResizeImageProps): Promise => { + return new Promise((resolve) => { + const reader = new FileReader(); + reader.readAsDataURL(file); + reader.onload = (event) => { + const img = new Image(); + img.src = event.target?.result as string; + img.onload = () => { + let width = img.width; + let height = img.height; + if (width > height) { + if (width > (options.maxWidth ?? 800)) { + height = Math.round((height * (options.maxWidth ?? 800)) / width); + width = options.maxWidth ?? 800; + } + } else if (height > (options.maxHeight ?? 800)) { + width = Math.round((width * (options.maxHeight ?? 800)) / height); + height = options.maxHeight ?? 800; + } + const canvas = document.createElement('canvas'); + canvas.width = width; + canvas.height = height; + const ctx = canvas.getContext('2d'); + ctx?.drawImage(img, 0, 0, width, height); + canvas.toBlob( + (blob) => { + if (!blob) return; + const resizedFile = new File([blob], file.name, { + type: 'imgage/jpeg', + lastModified: Date.now(), + }); + resolve(resizedFile); + }, + 'image/jpeg', + (options.quality && options.quality < 1 && options.quality > 0) + ? options.quality + : 0.8, + ); + }; + }; + }); +}; + +export { + deleteFiles, + getPublicUrl, + getSignedUrl, + listFiles, + resizeImage, + uploadFile, + updateFile +}; diff --git a/src/utils/supabase/client.ts b/src/utils/supabase/client.ts index 6e38d28..09b1b47 100644 --- a/src/utils/supabase/client.ts +++ b/src/utils/supabase/client.ts @@ -1,13 +1,12 @@ 'use client'; import { createBrowserClient } from '@supabase/ssr'; -import type { Database } from '@/utils/supabase/database.types'; -import type { SupabaseClient } from '@/utils/supabase/types'; +import type { Database, SupabaseClient } from '@/utils/supabase'; import { useMemo } from 'react'; let client: SupabaseClient | undefined; -const getSupbaseClient = () => { +const getSupbaseClient = (): SupabaseClient | undefined => { if (client) return client; client = createBrowserClient( process.env.NEXT_PUBLIC_SUPABASE_URL!, diff --git a/src/utils/supabase/server.ts b/src/utils/supabase/server.ts index 6b9d69c..72fd439 100644 --- a/src/utils/supabase/server.ts +++ b/src/utils/supabase/server.ts @@ -2,10 +2,10 @@ import 'server-only'; import { createServerClient } from '@supabase/ssr'; -import type { Database } from '@/utils/supabase/database.types'; +import type { Database, SupabaseClient } from '@/utils/supabase'; import { cookies } from 'next/headers'; -export const useSupabaseServer = async () => { +export const useSupabaseServer = async (): Promise => { const cookieStore = await cookies(); return createServerClient( process.env.NEXT_PUBLIC_SUPABASE_URL!,