Somewhat automate types and stuff.

This commit is contained in:
2025-05-14 12:59:00 -05:00
parent 68ba7cc41f
commit 0cf1049ec6
23 changed files with 595 additions and 64 deletions

View File

@ -2,14 +2,14 @@
import 'server-only';
import { encodedRedirect } from '@/utils/utils';
import { createClient } from '@/utils/supabase/server';
import { createServerClient } from '@/utils/supabase';
import { headers } from 'next/headers';
import { redirect } from 'next/navigation';
export const signUp = async (formData: FormData) => {
const email = formData.get('email') as string;
const password = formData.get('password') as string;
const supabase = await createClient();
const supabase = await createServerClient();
const origin = (await headers()).get('origin');
if (!email || !password) {
@ -47,7 +47,7 @@ export const signUp = async (formData: FormData) => {
export const signIn = async (formData: FormData) => {
const email = formData.get('email') as string;
const password = formData.get('password') as string;
const supabase = await createClient();
const supabase = await createServerClient();
const { error } = await supabase.auth.signInWithPassword({
email,
@ -63,7 +63,7 @@ export const signIn = async (formData: FormData) => {
export const forgotPassword = async (formData: FormData) => {
const email = formData.get('email') as string;
const supabase = await createClient();
const supabase = await createServerClient();
const origin = (await headers()).get('origin');
const callbackUrl = formData.get('callbackUrl') as string;
@ -96,7 +96,7 @@ export const forgotPassword = async (formData: FormData) => {
};
export const resetPassword = async (formData: FormData) => {
const supabase = await createClient();
const supabase = await createServerClient();
const password = formData.get('password') as string;
const confirmPassword = formData.get('confirmPassword') as string;
@ -132,7 +132,7 @@ export const resetPassword = async (formData: FormData) => {
};
export const signOut = async () => {
const supabase = await createClient();
const supabase = await createServerClient();
await supabase.auth.signOut();
return redirect('/sign-in');
};

View File

@ -1,4 +1,4 @@
'use server';
import 'server-only';
import { createClient } from '@/utils/supabase/server';
import { createServerClient } from '@/utils/supabase';

View File

@ -1,4 +1,4 @@
import { createClient } from '@/utils/supabase/server';
import { createServerClient } from '@/utils/supabase';
import { NextResponse } from 'next/server';
export async function GET(request: Request) {
@ -11,7 +11,7 @@ export async function GET(request: Request) {
const redirectTo = requestUrl.searchParams.get('redirect_to')?.toString();
if (code) {
const supabase = await createClient();
const supabase = await createServerClient();
await supabase.auth.exchangeCodeForSession(code);
}

View File

@ -1,12 +1,12 @@
'use server';
import { FetchDataSteps } from '@/components/tutorial';
import { createClient } from '@/utils/supabase/server';
import { createServerClient } from '@/utils/supabase';
import { InfoIcon } from 'lucide-react';
import { redirect } from 'next/navigation';
const ProtectedPage = async () => {
const supabase = await createClient();
const supabase = await createServerClient();
const {
data: { user },
} = await supabase.auth.getUser();

View File

@ -1,10 +1,10 @@
'use server';
import { createClient } from '@/utils/supabase/server';
import { createServerClient } from '@/utils/supabase';
import Image from 'next/image';
export default async function Page() {
const supabase = await createClient();
const supabase = await createServerClient();
// Get authenticated user
const {

View File

@ -3,7 +3,7 @@ import * as React from 'react';
import { ThemeProvider as NextThemesProvider } from 'next-themes';
import { Moon, Sun } from 'lucide-react';
import { useTheme } from 'next-themes';
import { Button } from '@/components/ui/button';
import { Button } from '@/components/ui';
export const ThemeProvider = ({
children,

View File

@ -2,11 +2,11 @@
import Link from 'next/link';
import { Button } from '@/components/ui';
import { createClient } from '@/utils/supabase/server';
import { createServerClient } from '@/utils/supabase';
import { signOut } from '@/actions/auth';
const NavigationAuth = async () => {
const supabase = await createClient();
const supabase = await createServerClient();
const {
data: { user },
} = await supabase.auth.getUser();

View File

@ -43,7 +43,7 @@ export default function Page() {
}
`.trim();
const FetchDataSteps = () => {
export const FetchDataSteps = () => {
return (
<ol className='flex flex-col gap-6'>
<TutorialStep title='Create some tables and insert some data'>
@ -93,4 +93,3 @@ const FetchDataSteps = () => {
</ol>
);
};
export default FetchDataSteps;

View File

@ -1,5 +1,3 @@
import { CodeBlock } from '@/components/tutorial/code-block';
import FetchDataSteps from '@/components/tutorial/fetch-data-steps';
import { TutorialStep } from '@/components/tutorial/tutorial-step';
export { CodeBlock, FetchDataSteps, TutorialStep };
export { CodeBlock } from '@/components/tutorial/code-block';
export { FetchDataSteps } from '@/components/tutorial/fetch-data-steps';
export { TutorialStep } from '@/components/tutorial/tutorial-step';

View File

@ -1,7 +1,5 @@
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Checkbox } from '@/components/ui/checkbox';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
export { Badge, Button, Checkbox, Input, Label };
export { Badge } from '@/components/ui/badge';
export { Button } from '@/components/ui/button';
export { Checkbox } from '@/components/ui/checkbox';
export { Input } from '@/components/ui/input';
export { Label } from '@/components/ui/label';

View File

View File

@ -2,7 +2,7 @@
create table profiles (
id uuid references auth.users on delete cascade not null primary key,
updated_at timestamp with time zone,
email text,
email text unique,
full_name text,
avatar_url text,
provider text,
@ -35,7 +35,7 @@ begin
new.id,
new.email,
new.raw_user_meta_data->>'full_name',
new.raw_user_meta_data->>'avatar_url'
new.raw_user_meta_data->>'avatar_url',
new.raw_user_meta_data->>'provider',
now()
);
@ -58,7 +58,6 @@ create policy "Avatar images are publicly accessible." on storage.objects
create policy "Anyone can upload an avatar." on storage.objects
for insert with check (bucket_id = 'avatars');
-- -- Create a table for public statuses
-- CREATE TABLE statuses (
-- id uuid DEFAULT gen_random_uuid() PRIMARY KEY,
@ -66,8 +65,7 @@ create policy "Anyone can upload an avatar." on storage.objects
-- updated_by_id uuid REFERENCES auth.users ON DELETE SET NULL DEFAULT auth.uid(),
-- created_at timestamp with time zone DEFAULT now() NOT NULL,
-- status text NOT NULL,
-- CONSTRAINT status_length CHECK (char_length(status) >= 3 AND char_length(status) <= 80),
-- CONSTRAINT statuses_user_id_fkey FOREIGN KEY (user_id) REFERENCES profiles(id) ON DELETE CASCADE
-- CONSTRAINT status_length CHECK (char_length(status) >= 3 AND char_length(status) <= 80)
-- );
-- -- Set up Row Level Security (RLS)

View File

@ -1,7 +1,8 @@
import { createBrowserClient } from '@supabase/ssr';
import type { Database } from '@/utils/supabase/types';
export const createClient = () =>
createBrowserClient(
createBrowserClient<Database>(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
);

View File

@ -0,0 +1,5 @@
export { createClient } from './client';
export { createClient as createServerClient } from './server';
export { updateSession } from './middleware';
export type * from './utils';
export type { Database } from './types';

View File

@ -1,5 +1,6 @@
import { createServerClient } from '@supabase/ssr';
import { type NextRequest, NextResponse } from 'next/server';
import type { Database } from '@/utils/supabase/types';
export const updateSession = async (request: NextRequest) => {
// This `try/catch` block is only here for the interactive tutorial.
@ -12,7 +13,7 @@ export const updateSession = async (request: NextRequest) => {
},
});
const supabase = createServerClient(
const supabase = createServerClient<Database>(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{

View File

@ -1,10 +1,11 @@
import { createServerClient } from '@supabase/ssr';
import type { Database } from '@/utils/supabase/types';
import { cookies } from 'next/headers';
export const createClient = async () => {
const cookieStore = await cookies();
return createServerClient(
return createServerClient<Database>(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{

188
src/utils/supabase/types.ts Normal file
View File

@ -0,0 +1,188 @@
export type Json =
| string
| number
| boolean
| null
| { [key: string]: Json | undefined }
| Json[];
export type Database = {
public: {
Tables: {
profiles: {
Row: {
avatar_url: string | null;
email: string | null;
full_name: string | null;
id: string;
provider: string | null;
updated_at: string | null;
};
Insert: {
avatar_url?: string | null;
email?: string | null;
full_name?: string | null;
id: string;
provider?: string | null;
updated_at?: string | null;
};
Update: {
avatar_url?: string | null;
email?: string | null;
full_name?: string | null;
id?: string;
provider?: string | null;
updated_at?: string | null;
};
Relationships: [];
};
statuses: {
Row: {
created_at: string;
id: string;
status: string;
updated_by_id: string | null;
user_id: string;
};
Insert: {
created_at?: string;
id?: string;
status: string;
updated_by_id?: string | null;
user_id: string;
};
Update: {
created_at?: string;
id?: string;
status?: string;
updated_by_id?: string | null;
user_id?: string;
};
Relationships: [];
};
};
Views: {
[_ in never]: never;
};
Functions: {
[_ in never]: never;
};
Enums: {
[_ in never]: never;
};
CompositeTypes: {
[_ in never]: never;
};
};
};
type DefaultSchema = Database[Extract<keyof Database, 'public'>];
export type Tables<
DefaultSchemaTableNameOrOptions extends
| keyof (DefaultSchema['Tables'] & DefaultSchema['Views'])
| { schema: keyof Database },
TableName extends DefaultSchemaTableNameOrOptions extends {
schema: keyof Database;
}
? keyof (Database[DefaultSchemaTableNameOrOptions['schema']]['Tables'] &
Database[DefaultSchemaTableNameOrOptions['schema']]['Views'])
: never = never,
> = DefaultSchemaTableNameOrOptions extends { schema: keyof Database }
? (Database[DefaultSchemaTableNameOrOptions['schema']]['Tables'] &
Database[DefaultSchemaTableNameOrOptions['schema']]['Views'])[TableName] extends {
Row: infer R;
}
? R
: never
: DefaultSchemaTableNameOrOptions extends keyof (DefaultSchema['Tables'] &
DefaultSchema['Views'])
? (DefaultSchema['Tables'] &
DefaultSchema['Views'])[DefaultSchemaTableNameOrOptions] extends {
Row: infer R;
}
? R
: never
: never;
export type TablesInsert<
DefaultSchemaTableNameOrOptions extends
| keyof DefaultSchema['Tables']
| { schema: keyof Database },
TableName extends DefaultSchemaTableNameOrOptions extends {
schema: keyof Database;
}
? keyof Database[DefaultSchemaTableNameOrOptions['schema']]['Tables']
: never = never,
> = DefaultSchemaTableNameOrOptions extends { schema: keyof Database }
? Database[DefaultSchemaTableNameOrOptions['schema']]['Tables'][TableName] extends {
Insert: infer I;
}
? I
: never
: DefaultSchemaTableNameOrOptions extends keyof DefaultSchema['Tables']
? DefaultSchema['Tables'][DefaultSchemaTableNameOrOptions] extends {
Insert: infer I;
}
? I
: never
: never;
export type TablesUpdate<
DefaultSchemaTableNameOrOptions extends
| keyof DefaultSchema['Tables']
| { schema: keyof Database },
TableName extends DefaultSchemaTableNameOrOptions extends {
schema: keyof Database;
}
? keyof Database[DefaultSchemaTableNameOrOptions['schema']]['Tables']
: never = never,
> = DefaultSchemaTableNameOrOptions extends { schema: keyof Database }
? Database[DefaultSchemaTableNameOrOptions['schema']]['Tables'][TableName] extends {
Update: infer U;
}
? U
: never
: DefaultSchemaTableNameOrOptions extends keyof DefaultSchema['Tables']
? DefaultSchema['Tables'][DefaultSchemaTableNameOrOptions] extends {
Update: infer U;
}
? U
: never
: never;
export type Enums<
DefaultSchemaEnumNameOrOptions extends
| keyof DefaultSchema['Enums']
| { schema: keyof Database },
EnumName extends DefaultSchemaEnumNameOrOptions extends {
schema: keyof Database;
}
? keyof Database[DefaultSchemaEnumNameOrOptions['schema']]['Enums']
: never = never,
> = DefaultSchemaEnumNameOrOptions extends { schema: keyof Database }
? Database[DefaultSchemaEnumNameOrOptions['schema']]['Enums'][EnumName]
: DefaultSchemaEnumNameOrOptions extends keyof DefaultSchema['Enums']
? DefaultSchema['Enums'][DefaultSchemaEnumNameOrOptions]
: never;
export type CompositeTypes<
PublicCompositeTypeNameOrOptions extends
| keyof DefaultSchema['CompositeTypes']
| { schema: keyof Database },
CompositeTypeName extends PublicCompositeTypeNameOrOptions extends {
schema: keyof Database;
}
? keyof Database[PublicCompositeTypeNameOrOptions['schema']]['CompositeTypes']
: never = never,
> = PublicCompositeTypeNameOrOptions extends { schema: keyof Database }
? Database[PublicCompositeTypeNameOrOptions['schema']]['CompositeTypes'][CompositeTypeName]
: PublicCompositeTypeNameOrOptions extends keyof DefaultSchema['CompositeTypes']
? DefaultSchema['CompositeTypes'][PublicCompositeTypeNameOrOptions]
: never;
export const Constants = {
public: {
Enums: {},
},
} as const;

View File

@ -0,0 +1,25 @@
import type { Database } from '@/utils/supabase/types';
// Table row types
export type Profile = Database['public']['Tables']['profiles']['Row'];
export type Status = Database['public']['Tables']['statuses']['Row'];
// Insert types
export type ProfileInsert = Database['public']['Tables']['profiles']['Insert'];
export type StatusInsert = Database['public']['Tables']['statuses']['Insert'];
// Update types
export type ProfileUpdate = Database['public']['Tables']['profiles']['Update'];
export type StatusUpdate = Database['public']['Tables']['statuses']['Update'];
// Generic helper to get any table's row type
export type TableRow<T extends keyof Database['public']['Tables']> =
Database['public']['Tables'][T]['Row'];
// Generic helper to get any table's insert type
export type TableInsert<T extends keyof Database['public']['Tables']> =
Database['public']['Tables'][T]['Insert'];
// Generic helper to get any table's update type
export type TableUpdate<T extends keyof Database['public']['Tables']> =
Database['public']['Tables'][T]['Update'];