Add some actions to make stuff easy
This commit is contained in:
@ -1,4 +0,0 @@
|
||||
'use server';
|
||||
|
||||
import 'server-only';
|
||||
import { createServerClient } from '@/utils/supabase';
|
@ -1,5 +1,5 @@
|
||||
import Link from 'next/link';
|
||||
import { forgotPassword } from '@/actions/auth';
|
||||
import { forgotPassword } from '@/lib/actions';
|
||||
import { FormMessage, type Message, SubmitButton } from '@/components/default';
|
||||
import { Input, Label } from '@/components/ui';
|
||||
import { SmtpMessage } from '@/app/(auth-pages)/smtp-message';
|
||||
|
@ -1,5 +1,5 @@
|
||||
import Link from 'next/link';
|
||||
import { signIn } from '@/actions/auth';
|
||||
import { signIn } from '@/lib/actions';
|
||||
import { FormMessage, type Message, SubmitButton } from '@/components/default';
|
||||
import { Input, Label } from '@/components/ui';
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import Link from 'next/link';
|
||||
import { signUp } from '@/actions/auth';
|
||||
import { signUp } from '@/lib/actions';
|
||||
import { FormMessage, type Message, SubmitButton } from '@/components/default';
|
||||
import { Input, Label } from '@/components/ui';
|
||||
import { SmtpMessage } from '@/app/(auth-pages)/smtp-message';
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { resetPassword } from '@/actions/auth';
|
||||
import { resetPassword } from '@/lib/actions';
|
||||
import { FormMessage, type Message, SubmitButton } from '@/components/default';
|
||||
import { Input, Label } from '@/components/ui';
|
||||
|
||||
|
@ -1,54 +1,21 @@
|
||||
'use server';
|
||||
|
||||
import { createServerClient } from '@/utils/supabase';
|
||||
import { getSignedUrl, getProfile } from '@/lib/actions';
|
||||
import Image from 'next/image';
|
||||
|
||||
export default async function Page() {
|
||||
const supabase = await createServerClient();
|
||||
const Page = async () => {
|
||||
const user = await getProfile();
|
||||
if (!user.success) throw new Error(user.error);
|
||||
|
||||
// Get authenticated user
|
||||
const {
|
||||
data: { user: authUser },
|
||||
error: userError,
|
||||
} = await supabase.auth.getUser();
|
||||
|
||||
if (userError || !authUser) {
|
||||
return (
|
||||
<div>
|
||||
Error loading user: {userError?.message ?? 'User not authenticated'}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Get user profile
|
||||
const { data: user, error: profileError } = await supabase
|
||||
.from('profiles')
|
||||
.select('*')
|
||||
.eq('id', authUser.id)
|
||||
.single();
|
||||
|
||||
if (profileError || !user) {
|
||||
return (
|
||||
<div>
|
||||
Error loading profile: {profileError?.message ?? 'Profile not found'}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Check if avatar URL exists
|
||||
if (!user.avatar_url) {
|
||||
return <div>No avatar image available</div>;
|
||||
}
|
||||
|
||||
// Get public URL for the avatar
|
||||
const { data: imageData } = await supabase.storage
|
||||
.from('avatars')
|
||||
.createSignedUrl(user.avatar_url, 3600);
|
||||
const imageUrl = await getSignedUrl({
|
||||
bucket: 'avatars',
|
||||
url: user.data.avatar_url ?? '',
|
||||
});
|
||||
if (!imageUrl.success) throw new Error(imageUrl.error);
|
||||
|
||||
return (
|
||||
<div className='flex justify-center items-center p-8'>
|
||||
<Image
|
||||
src={imageData?.signedUrl ?? '/images/default_pfp.png'}
|
||||
src={imageUrl.data ?? '/favicon.ico'}
|
||||
alt='User avatar'
|
||||
width={150}
|
||||
height={150}
|
||||
@ -57,4 +24,5 @@ export default async function Page() {
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
export default Page;
|
||||
|
@ -3,7 +3,7 @@
|
||||
import Link from 'next/link';
|
||||
import { Button } from '@/components/ui';
|
||||
import { createServerClient } from '@/utils/supabase';
|
||||
import { signOut } from '@/actions/auth';
|
||||
import { signOut } from '@/lib/actions';
|
||||
|
||||
const NavigationAuth = async () => {
|
||||
const supabase = await createServerClient();
|
||||
|
@ -136,3 +136,14 @@ export const signOut = async () => {
|
||||
await supabase.auth.signOut();
|
||||
return redirect('/sign-in');
|
||||
};
|
||||
|
||||
export const getUser = async () => {
|
||||
try {
|
||||
const supabase = await createServerClient();
|
||||
const { data, error } = await supabase.auth.getUser();
|
||||
if (error) throw error;
|
||||
return data.user;
|
||||
} catch (error) {
|
||||
console.error('Could not get user!', error);
|
||||
}
|
||||
};
|
7
src/lib/actions/index.ts
Normal file
7
src/lib/actions/index.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export * from './auth';
|
||||
export * from './storage';
|
||||
export * from './public';
|
||||
|
||||
export type Result<T> =
|
||||
| { success: true; data: T }
|
||||
| { success: false; error: string };
|
30
src/lib/actions/public.ts
Normal file
30
src/lib/actions/public.ts
Normal file
@ -0,0 +1,30 @@
|
||||
'use server';
|
||||
|
||||
import 'server-only';
|
||||
import { createServerClient, type Profile } from '@/utils/supabase';
|
||||
import { getUser } from '@/lib/actions';
|
||||
import type { Result } from './index';
|
||||
|
||||
export const getProfile = async (): Promise<Result<Profile>> => {
|
||||
try {
|
||||
const user = await getUser();
|
||||
if (!user) throw new Error('User not found');
|
||||
const supabase = await createServerClient();
|
||||
const { data, error } = await supabase
|
||||
.from('profiles')
|
||||
.select('*')
|
||||
.eq('id', user.id)
|
||||
.single();
|
||||
if (error) throw error;
|
||||
return { success: true, data: data as Profile };
|
||||
} catch (error) {
|
||||
console.error('Could not get profile!', error);
|
||||
return {
|
||||
success: false,
|
||||
error:
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: 'Unknown error getting profile',
|
||||
};
|
||||
}
|
||||
};
|
184
src/lib/actions/storage.ts
Normal file
184
src/lib/actions/storage.ts
Normal file
@ -0,0 +1,184 @@
|
||||
'use server';
|
||||
import 'server-only';
|
||||
import { createServerClient } from '@/utils/supabase';
|
||||
import type { Result } from './index';
|
||||
|
||||
export type GetStorageProps = {
|
||||
bucket: string;
|
||||
url: string;
|
||||
seconds?: number;
|
||||
transform?: {
|
||||
width?: number;
|
||||
height?: number;
|
||||
quality?: number;
|
||||
format?: 'origin';
|
||||
resize?: 'cover' | 'contain' | 'fill';
|
||||
};
|
||||
download?: boolean;
|
||||
};
|
||||
|
||||
export type UploadStorageProps = {
|
||||
bucket: string;
|
||||
path: string;
|
||||
file: File;
|
||||
options?: {
|
||||
cacheControl?: string;
|
||||
upsert?: boolean;
|
||||
contentType?: string;
|
||||
};
|
||||
};
|
||||
|
||||
export async function getSignedUrl({
|
||||
bucket,
|
||||
url,
|
||||
seconds = 3600,
|
||||
transform,
|
||||
download = false,
|
||||
}: GetStorageProps): Promise<Result<string>> {
|
||||
try {
|
||||
const supabase = await createServerClient();
|
||||
const { data, error } = await supabase.storage
|
||||
.from(bucket)
|
||||
.createSignedUrl(url, seconds, { transform });
|
||||
|
||||
if (error) throw error;
|
||||
if (!data?.signedUrl) throw new Error('No signed URL returned');
|
||||
|
||||
// Safely add download parameter if needed
|
||||
if (download) {
|
||||
const urlObj = new URL(data.signedUrl);
|
||||
urlObj.searchParams.append('download', '');
|
||||
return { success: true, data: urlObj.toString() };
|
||||
}
|
||||
|
||||
return { success: true, data: data.signedUrl };
|
||||
} catch (error) {
|
||||
console.error('Could not get signed URL for asset!', error);
|
||||
return {
|
||||
success: false,
|
||||
error:
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: 'Unknown error getting signed URL',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export async function getPublicUrl({
|
||||
bucket,
|
||||
url,
|
||||
transform = {},
|
||||
download = false,
|
||||
}: GetStorageProps): Promise<Result<string>> {
|
||||
try {
|
||||
const supabase = await createServerClient();
|
||||
const { data } = supabase.storage
|
||||
.from(bucket)
|
||||
.getPublicUrl(url, { transform });
|
||||
|
||||
if (!data?.publicUrl) throw new Error('No public URL returned');
|
||||
|
||||
// Safely add download parameter if needed
|
||||
if (download) {
|
||||
const urlObj = new URL(data.publicUrl);
|
||||
urlObj.searchParams.append('download', '');
|
||||
return { success: true, data: urlObj.toString() };
|
||||
}
|
||||
|
||||
return { success: true, data: data.publicUrl };
|
||||
} catch (error) {
|
||||
console.error('Could not get public URL for asset!', error);
|
||||
return {
|
||||
success: false,
|
||||
error:
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: 'Unknown error getting public URL',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export async function uploadFile({
|
||||
bucket,
|
||||
path,
|
||||
file,
|
||||
options = {},
|
||||
}: UploadStorageProps): Promise<Result<string>> {
|
||||
try {
|
||||
const supabase = await createServerClient();
|
||||
const { data, error } = await supabase.storage
|
||||
.from(bucket)
|
||||
.upload(path, file, options);
|
||||
|
||||
if (error) throw error;
|
||||
if (!data?.path) throw new Error('No path returned from upload');
|
||||
|
||||
return { success: true, data: data.path };
|
||||
} catch (error) {
|
||||
console.error('Could not upload file!', error);
|
||||
return {
|
||||
success: false,
|
||||
error:
|
||||
error instanceof Error ? error.message : 'Unknown error uploading file',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Add a helper to delete files
|
||||
export async function deleteFile({
|
||||
bucket,
|
||||
path,
|
||||
}: {
|
||||
bucket: string;
|
||||
path: string[];
|
||||
}): Promise<Result<null>> {
|
||||
try {
|
||||
const supabase = await createServerClient();
|
||||
const { error } = await supabase.storage.from(bucket).remove(path);
|
||||
|
||||
if (error) throw error;
|
||||
|
||||
return { success: true, data: null };
|
||||
} catch (error) {
|
||||
console.error('Could not delete file!', error);
|
||||
return {
|
||||
success: false,
|
||||
error:
|
||||
error instanceof Error ? error.message : 'Unknown error deleting file',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Add a helper to list files in a bucket
|
||||
export async function listFiles({
|
||||
bucket,
|
||||
path = '',
|
||||
options = {},
|
||||
}: {
|
||||
bucket: string;
|
||||
path?: string;
|
||||
options?: {
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
sortBy?: { column: string; order: 'asc' | 'desc' };
|
||||
};
|
||||
}): Promise<Result<Array<{ name: string; id: string; metadata: any }>>> {
|
||||
try {
|
||||
const supabase = await createServerClient();
|
||||
const { data, error } = await supabase.storage
|
||||
.from(bucket)
|
||||
.list(path, options);
|
||||
|
||||
if (error) throw error;
|
||||
if (!data) throw new Error('No data returned from list operation');
|
||||
|
||||
return { success: true, data };
|
||||
} catch (error) {
|
||||
console.error('Could not list files!', error);
|
||||
return {
|
||||
success: false,
|
||||
error:
|
||||
error instanceof Error ? error.message : 'Unknown error listing files',
|
||||
};
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user