Add default metadata & make replaceFile function

This commit is contained in:
Gabriel Brown 2025-06-01 16:36:29 -05:00
parent dc7cec8539
commit f49488123b
33 changed files with 194 additions and 51 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
public/icons/apple/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
public/icons/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

@ -2,7 +2,6 @@ import Link from 'next/link';
import { forgotPassword } from '@/lib/actions'; import { forgotPassword } from '@/lib/actions';
import { FormMessage, type Message, SubmitButton } from '@/components/default'; import { FormMessage, type Message, SubmitButton } from '@/components/default';
import { Input, Label } from '@/components/ui'; import { Input, Label } from '@/components/ui';
import { SmtpMessage } from '@/app/(auth-pages)/smtp-message';
const ForgotPassword = async (props: { searchParams: Promise<Message> }) => { const ForgotPassword = async (props: { searchParams: Promise<Message> }) => {
const searchParams = await props.searchParams; const searchParams = await props.searchParams;
@ -30,7 +29,6 @@ const ForgotPassword = async (props: { searchParams: Promise<Message> }) => {
<FormMessage message={searchParams} /> <FormMessage message={searchParams} />
</div> </div>
</form> </form>
<SmtpMessage />
</> </>
); );
}; };

View File

@ -1,25 +0,0 @@
import { ArrowUpRight, InfoIcon } from 'lucide-react';
import Link from 'next/link';
export const SmtpMessage = () => {
return (
<div className='bg-muted/50 px-5 py-3 border rounded-md flex gap-4'>
<InfoIcon size={16} className='mt-0.5' />
<div className='flex flex-col gap-1'>
<small className='text-sm text-secondary-foreground'>
<strong> Note:</strong> Emails are rate limited. Enable Custom SMTP to
increase the rate limit.
</small>
<div>
<Link
href='https://supabase.com/docs/guides/auth/auth-smtp'
target='_blank'
className='text-primary/50 hover:text-primary flex items-center text-sm gap-1'
>
Learn more <ArrowUpRight size={14} />
</Link>
</div>
</div>
</div>
);
};

View File

@ -9,24 +9,169 @@ import Footer from '@/components/default/footer';
import { Toaster } from '@/components/ui'; import { Toaster } from '@/components/ui';
export const metadata: Metadata = { export const metadata: Metadata = {
title: 'T3 Template with Supabase', title: {
description: 'Generated by create-t3-app', template: '%s | T3 Template',
icons: [ default: 'T3 Template with Supabase',
{ },
rel: 'icon', description: 'Created by Gib with T3!',
url: '/images/favicon.ico', applicationName: 'T3 Template',
keywords: 'T3 Template, Next.js, Supabase, Tailwind, TypeScript, React, T3, Gib, Theo',
authors: [{name: 'Gib', url: 'https://gbrown.org'}],
creator: 'Gib Brown',
publisher: 'Gib Brown',
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,
}, },
{ },
rel: 'icon', icons: {
type: 'image/png', icon: [
sizes: '32x32', { url: '/icons/favicon.ico', type: 'image/x-icon', sizes: 'any' },
url: '/images/favicon.png', { url: '/icons/favicon-16x16.png', type: 'image/png', sizes: '16x16'},
{ url: '/icons/favicon-32x32.png', type: 'image/png', sizes: '32x32'},
{ url: '/icons/favicon-96x96.png', type: 'image/png', sizes: '96x96'},
{ url: '/icons/favicon.ico', type: 'image/x-icon', sizes: 'any', media: '(prefers-color-scheme: dark)' },
{ url: '/icons/favicon-16x16.png', type: 'image/png', sizes: '16x16', media: '(prefers-color-scheme: dark)' },
{ url: '/icons/favicon-32x32.png', type: 'image/png', sizes: '32x32', media: '(prefers-color-scheme: dark)' },
{ url: '/icons/favicon-96x96.png', type: 'image/png', sizes: '96x96', media: '(prefers-color-scheme: dark)' },
{ url: '/icons/android/icon-36x36', type: 'image/png', sizes: '36x36'},
{ url: '/icons/android/icon-48x48', type: 'image/png', sizes: '48x48'},
{ url: '/icons/android/icon-72x72', type: 'image/png', sizes: '72x72'},
{ url: '/icons/android/icon-96x96', type: 'image/png', sizes: '96x96'},
{ url: '/icons/android/icon-144x144', type: 'image/png', sizes: '144x144'},
{ url: '/icons/android/icon-192x192', type: 'image/png', sizes: '192x192'},
{ url: '/icons/android/icon-36x36', type: 'image/png', sizes: '36x36', media: '(prefers-color-scheme: dark)' },
{ url: '/icons/android/icon-48x48', type: 'image/png', sizes: '48x48', media: '(prefers-color-scheme: dark)' },
{ url: '/icons/android/icon-72x72', type: 'image/png', sizes: '72x72', media: '(prefers-color-scheme: dark)' },
{ url: '/icons/android/icon-96x96', type: 'image/png', sizes: '96x96', media: '(prefers-color-scheme: dark)' },
{ url: '/icons/android/icon-144x144', type: 'image/png', sizes: '144x144', media: '(prefers-color-scheme: dark)' },
{ url: '/icons/android/icon-192x192', type: 'image/png', sizes: '192x192', media: '(prefers-color-scheme: dark)' },
],
shortcut: [
{ url: '/icons/android/icon-36x36', type: 'image/png', sizes: '36x36'},
{ url: '/icons/android/icon-48x48', type: 'image/png', sizes: '48x48'},
{ url: '/icons/android/icon-72x72', type: 'image/png', sizes: '72x72'},
{ url: '/icons/android/icon-96x96', type: 'image/png', sizes: '96x96'},
{ url: '/icons/android/icon-144x144', type: 'image/png', sizes: '144x144'},
{ url: '/icons/android/icon-192x192', type: 'image/png', sizes: '192x192'},
{ url: '/icons/android/icon-36x36', type: 'image/png', sizes: '36x36', media: '(prefers-color-scheme: dark)' },
{ url: '/icons/android/icon-48x48', type: 'image/png', sizes: '48x48', media: '(prefers-color-scheme: dark)' },
{ url: '/icons/android/icon-72x72', type: 'image/png', sizes: '72x72', media: '(prefers-color-scheme: dark)' },
{ url: '/icons/android/icon-96x96', type: 'image/png', sizes: '96x96', media: '(prefers-color-scheme: dark)' },
{ url: '/icons/android/icon-144x144', type: 'image/png', sizes: '144x144', media: '(prefers-color-scheme: dark)' },
{ url: '/icons/android/icon-192x192', type: 'image/png', sizes: '192x192', media: '(prefers-color-scheme: dark)' },
],
apple: [
{ url: '/icons/apple/icon.png', type: 'image/png', sizes: '192x192' },
{ url: '/icons/apple/icon-57x57.png', type: 'image/png', sizes: '57x57' },
{ url: '/icons/apple/icon-60x60.png', type: 'image/png', sizes: '60x60' },
{ url: '/icons/apple/icon-72x72.png', type: 'image/png', sizes: '72x72' },
{ url: '/icons/apple/icon-76x76.png', type: 'image/png', sizes: '76x76' },
{ url: '/icons/apple/icon-114x114.png', type: 'image/png', sizes: '114x114' },
{ url: '/icons/apple/icon-120x120.png', type: 'image/png', sizes: '120x120' },
{ url: '/icons/apple/icon-144x144.png', type: 'image/png', sizes: '144x144' },
{ url: '/icons/apple/icon-152x152.png', type: 'image/png', sizes: '152x152' },
{ url: '/icons/apple/icon-180x180.png', type: 'image/png', sizes: '180x180' },
{ url: '/icons/apple/icon.png', type: 'image/png', sizes: '192x192', media: '(prefers-color-scheme: dark)' },
{ url: '/icons/apple/icon-57x57.png', type: 'image/png', sizes: '57x57', media: '(prefers-color-scheme: dark)' },
{ url: '/icons/apple/icon-60x60.png', type: 'image/png', sizes: '60x60', media: '(prefers-color-scheme: dark)' },
{ url: '/icons/apple/icon-72x72.png', type: 'image/png', sizes: '72x72', media: '(prefers-color-scheme: dark)' },
{ url: '/icons/apple/icon-76x76.png', type: 'image/png', sizes: '76x76', media: '(prefers-color-scheme: dark)' },
{ url: '/icons/apple/icon-114x114.png', type: 'image/png', sizes: '114x114', media: '(prefers-color-scheme: dark)' },
{ url: '/icons/apple/icon-120x120.png', type: 'image/png', sizes: '120x120', media: '(prefers-color-scheme: dark)' },
{ url: '/icons/apple/icon-144x144.png', type: 'image/png', sizes: '144x144', media: '(prefers-color-scheme: dark)' },
{ url: '/icons/apple/icon-152x152.png', type: 'image/png', sizes: '152x152', media: '(prefers-color-scheme: dark)' },
{ url: '/icons/apple/icon-180x180.png', type: 'image/png', sizes: '180x180', media: '(prefers-color-scheme: dark)' },
],
other: [
{
rel: 'apple-touch-icon-precomposed',
url: '/icons/apple/icon-precomposed.png',
type: 'image/png',
sizes: '180x180'
},
],
},
twitter: {
card: 'app',
title: 'T3 Template',
description: 'Created by Gib with T3!',
siteId: '',
creator: '@cs_gib',
creatorId: '',
images: {
url: 'https://git.gbrown.org/gib/T3-Template/raw/main/public/icons/apple/icon.png',
alt: 'T3 Template',
}, },
{ app: {
rel: 'apple-touch-icon', name: 'T3 Template',
url: '/images/appicon.png', id: {
iphone: '',
ipad: '',
googleplay: '',
},
url: {
iphone: '',
ipad: '',
googleplay: '',
},
}, },
], },
verification: {
google: 'google',
yandex: 'yandex',
yahoo: 'yahoo',
},
itunes: {
appId: '',
appArgument: '',
},
appleWebApp: {
title: 'T3 Template',
statusBarStyle: 'black-translucent',
startupImage: [
'/icons/apple/splash-768x1004.png',
{
url: '/icons/apple/splash-1536x2008.png',
media: '(device-width: 768px) and (device-height: 1024px)',
},
],
},
appLinks: {
ios: {
url: 'https://t3-template.gbrown.org/ios',
app_store_id: 't3_template',
},
android: {
package: 'org.gbrown.android/t3-template',
app_name: 'app_t3_template',
},
web: {
url: 'https://t3-template.gbrown.org/web',
should_fallback: true,
},
},
facebook: {
appId: '',
},
pinterest: {
richPin: true,
},
category: 'technology',
}; };
const geist = Geist({ const geist = Geist({

View File

@ -4,6 +4,7 @@ import Link from 'next/link';
import { Button } from '@/components/ui'; import { Button } from '@/components/ui';
import NavigationAuth from './auth'; import NavigationAuth from './auth';
import { ThemeToggle } from '@/components/context/theme'; import { ThemeToggle } from '@/components/context/theme';
import Image from 'next/image';
const Navigation = () => { const Navigation = () => {
return ( return (
@ -16,7 +17,10 @@ const Navigation = () => {
items-center p-3 px-5 text-sm' items-center p-3 px-5 text-sm'
> >
<div className='flex gap-5 items-center font-semibold'> <div className='flex gap-5 items-center font-semibold'>
<Link href={'/'}>T3 Supabase Template</Link> <Link className='flex flex-row my-auto gap-2' href='/'>
<Image src='/icons/favicon-96x96.png' alt='T3 Logo' width={50} height={50} />
<h1 className='my-auto text-2xl'>T3 Supabase Template</h1>
</Link>
<div className='flex items-center gap-2'> <div className='flex items-center gap-2'>
<Button asChild> <Button asChild>
<Link href='https://git.gbrown.org/gib/T3-Template'> <Link href='https://git.gbrown.org/gib/T3-Template'>

View File

@ -31,7 +31,6 @@ export type UploadStorageProps = {
export type ReplaceStorageProps = { export type ReplaceStorageProps = {
bucket: string; bucket: string;
prevPath: string; prevPath: string;
path: string;
file: File; file: File;
options?: { options?: {
cacheControl?: string; cacheControl?: string;
@ -137,21 +136,22 @@ export const uploadFile = async ({
export const replaceFile = async ({ export const replaceFile = async ({
bucket, bucket,
prevPath, prevPath,
path,
file, file,
options = {}, options = {},
}: ReplaceStorageProps): Promise<Result<string>> => { }: ReplaceStorageProps): Promise<Result<string>> => {
try { try {
options.upsert = true;
const supabase = await createServerClient(); const supabase = await createServerClient();
//const deleteFileData = await deleteFile({
//bucket,
//path: [...prevPath],
//});
const { data, error } = await supabase.storage const { data, error } = await supabase.storage
.from(bucket) .from(bucket)
.update(path, file, options); //.update(path, file, options);
.update(prevPath, file, options);
if (error) throw error; if (error) throw error;
if (!data?.path) throw new Error('No path returned from upload'); if (!data?.path) throw new Error('No path returned from upload');
const deleteFileData = await deleteFile({
bucket,
path: [...prevPath],
});
return { success: true, data: data.path }; return { success: true, data: data.path };
} catch (error) { } catch (error) {
return { return {

View File

@ -1,7 +1,7 @@
'use client' 'use client'
import { useState, useRef } from 'react'; import { useState, useRef } from 'react';
import { deleteFile, uploadFile } from '@/lib/actions'; import { deleteFile, replaceFile, uploadFile } from '@/lib/actions';
import { toast } from 'sonner'; import { toast } from 'sonner';
import { useAuth } from '@/components/context/auth'; import { useAuth } from '@/components/context/auth';
import { resizeImage } from '@/lib/hooks'; import { resizeImage } from '@/lib/hooks';
@ -43,6 +43,21 @@ export const useFileUpload = () => {
console.error('Error deleting file:', deleteResult.error); console.error('Error deleting file:', deleteResult.error);
throw new Error(deleteResult.error || `Failed to delete ${prevPath}`); throw new Error(deleteResult.error || `Failed to delete ${prevPath}`);
} else console.log('Delete sucessful!') } else console.log('Delete sucessful!')
console.log('Deleted file path: ', deleteResult.data)
const updateResult = await replaceFile({
bucket,
prevPath: prevPath,
file,
options: {
contentType: file.type,
},
});
if (!updateResult.success) {
console.error('Error updating file:', updateResult.error);
} else {
console.log('We used the new update function hopefully it worked!');
return { success: true, path: updateResult.data };
}
} }
let fileToUpload = file; let fileToUpload = file;

View File

@ -58,6 +58,12 @@ create policy "Avatar images are publicly accessible." on storage.objects
create policy "Anyone can upload an avatar." on storage.objects create policy "Anyone can upload an avatar." on storage.objects
for insert with check (bucket_id = 'avatars'); for insert with check (bucket_id = 'avatars');
create policy "Anyone can update an avatar." on storage.objects
for update using (bucket_id = 'avatars');
create policy "Anyone can delete an avatar." on storage.objects
for delete using (bucket_id = 'avatars');
-- -- Create a table for public statuses -- -- Create a table for public statuses
-- CREATE TABLE statuses ( -- CREATE TABLE statuses (
-- id uuid DEFAULT gen_random_uuid() PRIMARY KEY, -- id uuid DEFAULT gen_random_uuid() PRIMARY KEY,