Somewhat automate types and stuff.
This commit is contained in:
parent
68ba7cc41f
commit
0cf1049ec6
@ -19,3 +19,9 @@ NODE_ENV=
|
||||
# Client Variables
|
||||
NEXT_PUBLIC_SUPABASE_URL=
|
||||
NEXT_PUBLIC_SUPABASE_ANON_KEY=
|
||||
|
||||
# Script Variables # Default Values
|
||||
SUPABASE_DB_PASSWORD=
|
||||
#SUPABASE_DB_PORT= # 5432
|
||||
#SUPABASE_DB_USER= # postgres
|
||||
#SUPABASE_DB_NAME= # postgres
|
||||
|
40
README.md
40
README.md
@ -1,29 +1,19 @@
|
||||
# Create T3 App
|
||||
# T3 Template with Self Hosted Supabase
|
||||
|
||||
This is a [T3 Stack](https://create.t3.gg/) project bootstrapped with `create-t3-app`.
|
||||
This is my template for self hosting both Next.js & Supabase in order to create a perfect app!!
|
||||
|
||||
## What's next? How do I make an app with this?
|
||||
## What to do
|
||||
|
||||
We try to keep this project as simple as possible, so you can start with just the scaffolding we set up for you, and add additional things later when they become necessary.
|
||||
- [Self Host Supabase](https://supabase.com/docs/guides/self-hosting/docker)
|
||||
- You will need to make sure you have some way to connect to the postgres database from the host. I had to remove the database port from the supabase-pooler and add it to the supabase-db in order to directly connect to it. This will be important for generating our types.
|
||||
- Clone this repo.
|
||||
- Go to src/server/db/schema.sql & run this SQL in the SQL editor on the Web UI of your Supabase instance.
|
||||
- Generate your types
|
||||
- This part is potentially super weird if you are self hosting. If you are connecting directly to your database that you plan to use for production, you will need to clone your repo on the host running supabase so that you can then use the supabase cli tool. Once you have done that, you will need to install the supabase-cli tool with sudo. I just run something like `sudo npx supabase --help` and then accept the prompt to install the program. Once you have done this, you can then run the following command, replacing the password and the port to match your supabase database:
|
||||
|
||||
If you are not familiar with the different technologies used in this project, please refer to the respective docs. If you still are in the wind, please join our [Discord](https://t3.gg/discord) and ask for help.
|
||||
|
||||
- [Next.js](https://nextjs.org)
|
||||
- [NextAuth.js](https://next-auth.js.org)
|
||||
- [Prisma](https://prisma.io)
|
||||
- [Drizzle](https://orm.drizzle.team)
|
||||
- [Tailwind CSS](https://tailwindcss.com)
|
||||
- [tRPC](https://trpc.io)
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about the [T3 Stack](https://create.t3.gg/), take a look at the following resources:
|
||||
|
||||
- [Documentation](https://create.t3.gg/)
|
||||
- [Learn the T3 Stack](https://create.t3.gg/en/faq#what-learning-resources-are-currently-available) — Check out these awesome tutorials
|
||||
|
||||
You can check out the [create-t3-app GitHub repository](https://github.com/t3-oss/create-t3-app) — your feedback and contributions are welcome!
|
||||
|
||||
## How do I deploy this?
|
||||
|
||||
Follow our deployment guides for [Vercel](https://create.t3.gg/en/deployment/vercel), [Netlify](https://create.t3.gg/en/deployment/netlify) and [Docker](https://create.t3.gg/en/deployment/docker) for more information.
|
||||
```bash
|
||||
sudo npx supabase gen types typescript \
|
||||
--db-url "postgres://postgres:password@localhost:5432/postgres" \
|
||||
--schema public \
|
||||
> ./src/lib/types
|
||||
```
|
||||
|
@ -52,5 +52,5 @@
|
||||
"ct3aMetadata": {
|
||||
"initVersion": "7.39.3"
|
||||
},
|
||||
"packageManager": "pnpm@10.10.0"
|
||||
"packageManager": "pnpm@10.11.0+sha512.6540583f41cc5f628eb3d9773ecee802f4f9ef9923cc45b69890fb47991d4b092964694ec3a4f738a420c918a333062c8b925d312f42e4f0c263eb603551f977"
|
||||
}
|
||||
|
133
scripts/generate_types.sh
Executable file
133
scripts/generate_types.sh
Executable file
@ -0,0 +1,133 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Define colors for better output
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
BLUE='\033[0;34m'
|
||||
BOLD='\033[1m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Get the project root directory (one level up from scripts/)
|
||||
PROJECT_ROOT="$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")"
|
||||
|
||||
# Clear the screen for better visibility
|
||||
clear
|
||||
|
||||
echo -e "${BOLD}${BLUE}===== Supabase TypeScript Type Generator =====${NC}"
|
||||
echo
|
||||
echo -e "${YELLOW}⚠️ IMPORTANT: This script must be run on the server hosting the Supabase Docker container.${NC}"
|
||||
echo -e "It will not work if you're running it from a different machine, even if connected via VPN."
|
||||
echo
|
||||
echo -e "Project root: ${BLUE}${PROJECT_ROOT}${NC}"
|
||||
echo
|
||||
|
||||
# Ask for confirmation
|
||||
read -p "Are you running this script on the Supabase host server? (y/n) " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
echo -e "${RED}Aborted. Please run this script on the server hosting Supabase.${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check for sudo access
|
||||
if ! sudo -v; then
|
||||
echo -e "${RED}Error: This script requires sudo privileges.${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if .env file exists in project root
|
||||
ENV_FILE="${PROJECT_ROOT}/.env"
|
||||
if [ ! -f "$ENV_FILE" ]; then
|
||||
echo -e "${RED}Error: .env file not found at ${ENV_FILE}${NC}"
|
||||
echo -e "Please create a .env file with the following variables:"
|
||||
echo -e "SUPABASE_DB_HOST, SUPABASE_DB_PORT, SUPABASE_DB_USER, SUPABASE_DB_PASSWORD, SUPABASE_DB_NAME"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}Found .env file at $ENV_FILE${NC}"
|
||||
|
||||
# Source the .env file to get environment variables
|
||||
export $(grep -v '^#' $ENV_FILE | xargs)
|
||||
|
||||
# Check if required variables are set
|
||||
if [ -z "$SUPABASE_DB_HOST" ] || [ -z "$SUPABASE_DB_PORT" ] || [ -z "$SUPABASE_DB_USER" ] || [ -z "$SUPABASE_DB_PASSWORD" ] || [ -z "$SUPABASE_DB_NAME" ]; then
|
||||
# Try to use default variables if Supabase-specific ones aren't set
|
||||
if [ -z "$SUPABASE_DB_HOST" ]; then SUPABASE_DB_HOST=${DB_HOST:-localhost}; fi
|
||||
if [ -z "$SUPABASE_DB_PORT" ]; then SUPABASE_DB_PORT=${DB_PORT:-5432}; fi
|
||||
if [ -z "$SUPABASE_DB_USER" ]; then SUPABASE_DB_USER=${DB_USER:-postgres}; fi
|
||||
if [ -z "$SUPABASE_DB_PASSWORD" ]; then SUPABASE_DB_PASSWORD=${DB_PASSWORD}; fi
|
||||
if [ -z "$SUPABASE_DB_NAME" ]; then SUPABASE_DB_NAME=${DB_NAME:-postgres}; fi
|
||||
|
||||
# Check again after trying defaults
|
||||
if [ -z "$SUPABASE_DB_HOST" ] || [ -z "$SUPABASE_DB_PORT" ] || [ -z "$SUPABASE_DB_USER" ] || [ -z "$SUPABASE_DB_PASSWORD" ] || [ -z "$SUPABASE_DB_NAME" ]; then
|
||||
echo -e "${RED}Error: Missing required environment variables${NC}"
|
||||
echo -e "Please ensure your .env file contains:"
|
||||
echo -e "SUPABASE_DB_HOST, SUPABASE_DB_PORT, SUPABASE_DB_USER, SUPABASE_DB_PASSWORD, SUPABASE_DB_NAME"
|
||||
echo -e "Or the equivalent DB_* variables"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check if supabase CLI is installed for the sudo user
|
||||
echo -e "${YELLOW}Checking if Supabase CLI is installed...${NC}"
|
||||
if ! sudo npx supabase --version &>/dev/null; then
|
||||
echo -e "${YELLOW}Supabase CLI not found. Installing...${NC}"
|
||||
sudo npm install -g supabase
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -e "${RED}Failed to install Supabase CLI. Please install it manually:${NC}"
|
||||
echo -e "sudo npm install -g supabase"
|
||||
exit 1
|
||||
fi
|
||||
echo -e "${GREEN}Supabase CLI installed successfully.${NC}"
|
||||
else
|
||||
echo -e "${GREEN}Supabase CLI is already installed.${NC}"
|
||||
fi
|
||||
|
||||
echo -e "${YELLOW}Generating Supabase TypeScript types...${NC}"
|
||||
|
||||
# Construct the database URL from environment variables
|
||||
DB_URL="postgres://$SUPABASE_DB_USER:$SUPABASE_DB_PASSWORD@$SUPABASE_DB_HOST:$SUPABASE_DB_PORT/$SUPABASE_DB_NAME"
|
||||
|
||||
# Determine the output directory (relative to project root)
|
||||
OUTPUT_DIR="${PROJECT_ROOT}/utils/supabase"
|
||||
if [ ! -d "$OUTPUT_DIR" ]; then
|
||||
echo -e "${YELLOW}Output directory $OUTPUT_DIR not found. Creating...${NC}"
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
fi
|
||||
|
||||
# Create a temporary file for the output
|
||||
TEMP_FILE=$(mktemp)
|
||||
|
||||
# Run the Supabase CLI command with sudo
|
||||
echo -e "${YELLOW}Running Supabase CLI to generate types...${NC}"
|
||||
sudo -E npx supabase gen types typescript \
|
||||
--db-url "$DB_URL" \
|
||||
--schema public > "$TEMP_FILE" 2>&1
|
||||
|
||||
# Check if the command was successful
|
||||
if [ $? -eq 0 ] && [ -s "$TEMP_FILE" ] && ! grep -q "Error" "$TEMP_FILE"; then
|
||||
# Move the temp file to the final destination
|
||||
mv "$TEMP_FILE" "$OUTPUT_DIR/types.ts"
|
||||
echo -e "${GREEN}✓ TypeScript types successfully generated at $OUTPUT_DIR/types.ts${NC}"
|
||||
|
||||
# Show the first few lines to confirm it looks right
|
||||
echo -e "${YELLOW}Preview of generated types:${NC}"
|
||||
head -n 10 "$OUTPUT_DIR/types.ts"
|
||||
echo -e "${YELLOW}...${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ Failed to generate TypeScript types${NC}"
|
||||
echo -e "${RED}Error output:${NC}"
|
||||
cat "$TEMP_FILE"
|
||||
rm "$TEMP_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Clear sensitive environment variables
|
||||
unset SUPABASE_DB_PASSWORD
|
||||
unset DB_URL
|
||||
|
||||
echo -e "${GREEN}${BOLD}Type generation complete!${NC}"
|
||||
echo -e "You can now use these types in your Next.js application."
|
||||
echo -e "Import them with: ${BLUE}import { Database } from '@/utils/supabase/types'${NC}"
|
@ -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');
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
'use server';
|
||||
|
||||
import 'server-only';
|
||||
import { createClient } from '@/utils/supabase/server';
|
||||
import { createServerClient } from '@/utils/supabase';
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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';
|
||||
|
@ -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';
|
||||
|
@ -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)
|
||||
|
@ -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!,
|
||||
);
|
||||
|
5
src/utils/supabase/index.ts
Normal file
5
src/utils/supabase/index.ts
Normal 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';
|
@ -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!,
|
||||
{
|
||||
|
@ -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
188
src/utils/supabase/types.ts
Normal 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;
|
25
src/utils/supabase/utils.ts
Normal file
25
src/utils/supabase/utils.ts
Normal 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'];
|
188
utils/supabase/types.ts
Normal file
188
utils/supabase/types.ts
Normal 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;
|
Loading…
x
Reference in New Issue
Block a user