diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..787a424
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,2 @@
+NEXT_PUBLIC_SUPABASE_URL=
+NEXT_PUBLIC_SUPABASE_ANON_KEY=
\ No newline at end of file
diff --git a/app/(auth)/auth/callback/route.ts b/app/(auth)/auth/callback/route.ts
new file mode 100644
index 0000000..819d676
--- /dev/null
+++ b/app/(auth)/auth/callback/route.ts
@@ -0,0 +1,19 @@
+import type { NextRequest } from 'next/server';
+
+import { createSupabaseServerClient } from '@/lib/services/supabase/server';
+import { cookies } from 'next/headers';
+import { NextResponse } from 'next/server';
+
+export async function GET(request: NextRequest) {
+ const requestUrl = new URL(request.url);
+ const code = requestUrl.searchParams.get('code');
+ if (code) {
+ const cookieStore = cookies();
+ const supabase = createSupabaseServerClient(cookieStore);
+
+ await supabase.auth.exchangeCodeForSession(code);
+ }
+
+ // URL to redirect to after sign in process completes
+ return NextResponse.redirect(requestUrl.origin);
+}
diff --git a/app/(auth)/auth/sign-in/route.ts b/app/(auth)/auth/sign-in/route.ts
new file mode 100644
index 0000000..5b1b834
--- /dev/null
+++ b/app/(auth)/auth/sign-in/route.ts
@@ -0,0 +1,23 @@
+import type { NextRequest } from 'next/server';
+
+import { createSupabaseServerClient } from '@/lib/services/supabase/server';
+import { cookies } from 'next/headers';
+import { NextResponse } from 'next/server';
+
+export async function POST(request: NextRequest) {
+ const supabase = createSupabaseServerClient(cookies());
+ const redirectURL = new URL('/auth/callback', request.url);
+
+ const scopes = 'user-read-email user-read-playback-position';
+ const result = await supabase.auth.signInWithOAuth({
+ options: {
+ redirectTo: redirectURL.toString(),
+ scopes,
+ },
+ provider: 'spotify',
+ });
+
+ return NextResponse.redirect(result.data.url!, {
+ status: 301,
+ });
+}
diff --git a/app/(auth)/sign-in/page.tsx b/app/(auth)/sign-in/page.tsx
new file mode 100644
index 0000000..468b058
--- /dev/null
+++ b/app/(auth)/sign-in/page.tsx
@@ -0,0 +1,9 @@
+import { Button } from '@radix-ui/themes';
+
+export default function Page() {
+ return (
+
+ );
+}
diff --git a/env.mjs b/env.mjs
index 5793354..32ea9a1 100644
--- a/env.mjs
+++ b/env.mjs
@@ -16,6 +16,11 @@ const getGitSha = () => {
const envSchema = z.object({
GIT_SHA: z.string().min(1).default(getGitSha()),
+ NEXT_PUBLIC_SUPABASE_ANON_KEY: z.string(),
+ NEXT_PUBLIC_SUPABASE_URL: z.string(),
+ // We don't have these configurations yet.
+ SUPABASE_SERVICE_ROLE_KEY: z.string().default(''),
+ SUPABASE_URL: z.string().default(''),
});
const envValidation = envSchema.safeParse(process.env);
diff --git a/lib/services/supabase/auth.ts b/lib/services/supabase/auth.ts
new file mode 100644
index 0000000..89b4a88
--- /dev/null
+++ b/lib/services/supabase/auth.ts
@@ -0,0 +1,63 @@
+import type { Database } from '@/types/database';
+import type { CookieOptions } from '@supabase/ssr';
+import type { NextRequest } from 'next/server';
+
+import { env } from '@/env.mjs';
+import { createServerClient } from '@supabase/ssr';
+import { NextResponse } from 'next/server';
+
+export const getSupabaseAuthSession = async (request: NextRequest) => {
+ let response = NextResponse.next({
+ request: {
+ headers: request.headers,
+ },
+ });
+
+ const supabase = createServerClient(
+ env.NEXT_PUBLIC_SUPABASE_URL,
+ env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
+ {
+ cookies: {
+ get(name: string) {
+ return request.cookies.get(name)?.value;
+ },
+ remove(name, options) {
+ request.cookies.delete({
+ name,
+ ...options,
+ });
+ response = NextResponse.next({
+ request: {
+ headers: request.headers,
+ },
+ });
+ response.cookies.delete({
+ name,
+ ...options,
+ });
+ },
+ set(name: string, value: string, options: CookieOptions) {
+ request.cookies.set({
+ name,
+ value,
+ ...options,
+ });
+ response = NextResponse.next({
+ request: {
+ headers: request.headers,
+ },
+ });
+ response.cookies.set({
+ name,
+ value,
+ ...options,
+ });
+ },
+ },
+ },
+ );
+
+ const authSession = await supabase.auth.getSession();
+
+ return { authSession, response };
+};
diff --git a/lib/services/supabase/browser.ts b/lib/services/supabase/browser.ts
new file mode 100644
index 0000000..dbcd75e
--- /dev/null
+++ b/lib/services/supabase/browser.ts
@@ -0,0 +1,11 @@
+'use client';
+import type { Database } from '@/types/database';
+
+import { env } from '@/env.mjs';
+import { createBrowserClient } from '@supabase/ssr';
+
+export const createSupabaseBrowserClient = () =>
+ createBrowserClient(
+ env.NEXT_PUBLIC_SUPABASE_URL,
+ env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
+ );
diff --git a/lib/services/supabase/server.ts b/lib/services/supabase/server.ts
new file mode 100644
index 0000000..92e5c46
--- /dev/null
+++ b/lib/services/supabase/server.ts
@@ -0,0 +1,39 @@
+import type { Database } from '@/types/database';
+import type { CookieOptions } from '@supabase/ssr';
+import type { cookies } from 'next/headers';
+
+import { env } from '@/env.mjs';
+import { createServerClient } from '@supabase/ssr';
+
+export const createSupabaseServerClient = (
+ cookieStore: ReturnType,
+) =>
+ createServerClient(
+ env.NEXT_PUBLIC_SUPABASE_URL,
+ env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
+ {
+ cookies: {
+ get(name: string) {
+ return cookieStore.get(name)?.value;
+ },
+ remove(name: string, options: CookieOptions) {
+ try {
+ cookieStore.delete({ name, ...options });
+ } catch (error) {
+ // The `delete` method was called from a Server Component.
+ // This can be ignored if you have middleware refreshing
+ // user sessions.
+ }
+ },
+ set(name: string, value: string, options: CookieOptions) {
+ try {
+ cookieStore.set({ name, value, ...options });
+ } catch (error) {
+ // The `set` method was called from a Server Component.
+ // This can be ignored if you have middleware refreshing
+ // user sessions.
+ }
+ },
+ },
+ },
+ );
diff --git a/lib/services/supabase/service.ts b/lib/services/supabase/service.ts
new file mode 100644
index 0000000..348ba40
--- /dev/null
+++ b/lib/services/supabase/service.ts
@@ -0,0 +1,7 @@
+import type { Database } from '@/types/database';
+
+import { env } from '@/env.mjs';
+import { createClient } from '@supabase/supabase-js';
+
+export const createSupabaseServiceClient = () =>
+ createClient(env.SUPABASE_URL, env.SUPABASE_SERVICE_ROLE_KEY);
diff --git a/middleware.ts b/middleware.ts
new file mode 100644
index 0000000..3b8d15b
--- /dev/null
+++ b/middleware.ts
@@ -0,0 +1,29 @@
+import type { NextRequest } from 'next/server';
+
+import { getSupabaseAuthSession } from '@/lib/services/supabase/auth';
+import { NextResponse } from 'next/server';
+
+const publicRoutes = ['/', '/auth/callback', '/auth/sign-in', '/sign-in'];
+
+export async function middleware(request: NextRequest) {
+ const { pathname } = new URL(request.url);
+
+ if (publicRoutes.includes(pathname)) {
+ return NextResponse.next({
+ request: {
+ headers: request.headers,
+ },
+ });
+ }
+
+ const { authSession, response } = await getSupabaseAuthSession(request);
+ if (authSession.data.session) {
+ return response;
+ }
+
+ return NextResponse.redirect(new URL('/sign-in', request.url));
+}
+
+export const config = {
+ matcher: ['/((?!_next/static|_next/image|favicon.ico|logo.png|logo.svg).*)'],
+};
diff --git a/package.json b/package.json
index e6eef42..e39013d 100644
--- a/package.json
+++ b/package.json
@@ -11,12 +11,16 @@
"prepare": "is-ci || husky install",
"start": "next start",
"storybook": "storybook dev -p 6006",
+ "supabase:login": "supabase login",
+ "supabase:types": "supabase gen types typescript --project-id zcetythxtznisfgaakyk > types/database.ts",
"typecheck": "tsc --noEmit"
},
"prettier": "@vercel/style-guide/prettier",
"dependencies": {
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/themes": "^2.0.2",
+ "@supabase/ssr": "^0.0.10",
+ "@supabase/supabase-js": "^2.39.0",
"next": "14.0.4",
"next-themes": "1.0.0-beta.0",
"react": "^18",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index caa3180..4d10da5 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -11,6 +11,12 @@ dependencies:
'@radix-ui/themes':
specifier: ^2.0.2
version: 2.0.2(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0)
+ '@supabase/ssr':
+ specifier: ^0.0.10
+ version: 0.0.10(@supabase/supabase-js@2.39.0)
+ '@supabase/supabase-js':
+ specifier: ^2.39.0
+ version: 2.39.0
next:
specifier: 14.0.4
version: 14.0.4(@babel/core@7.23.5)(react-dom@18.2.0)(react@18.2.0)
@@ -4439,6 +4445,73 @@ packages:
file-system-cache: 2.3.0
dev: true
+ /@supabase/functions-js@2.1.5:
+ resolution: {integrity: sha512-BNzC5XhCzzCaggJ8s53DP+WeHHGT/NfTsx2wUSSGKR2/ikLFQTBCDzMvGz/PxYMqRko/LwncQtKXGOYp1PkPaw==}
+ dependencies:
+ '@supabase/node-fetch': 2.6.15
+ dev: false
+
+ /@supabase/gotrue-js@2.60.1:
+ resolution: {integrity: sha512-dM28NhyPS5NLWpJbVokxGbuEMmMK2K+EBXYlNU2NEYzp1BrkdxetNh8ucslMbKauJ93XAEhbMCQHSO9fZ2E+DQ==}
+ dependencies:
+ '@supabase/node-fetch': 2.6.15
+ dev: false
+
+ /@supabase/node-fetch@2.6.15:
+ resolution: {integrity: sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ==}
+ engines: {node: 4.x || >=6.0.0}
+ dependencies:
+ whatwg-url: 5.0.0
+ dev: false
+
+ /@supabase/postgrest-js@1.9.0:
+ resolution: {integrity: sha512-axP6cU69jDrLbfihJKQ6vU27tklD0gzb9idkMN363MtTXeJVt5DQNT3JnJ58JVNBdL74hgm26rAsFNvHk+tnSw==}
+ dependencies:
+ '@supabase/node-fetch': 2.6.15
+ dev: false
+
+ /@supabase/realtime-js@2.9.0:
+ resolution: {integrity: sha512-e/SI+/eqFJorAKAgVAwKQ9hSDQSBp86Yh7XbQmfJJ90LEfpM52HlTfJt/03lcepRu6BmH5h1uYn1b4zta7ghdw==}
+ dependencies:
+ '@supabase/node-fetch': 2.6.15
+ '@types/phoenix': 1.6.4
+ '@types/websocket': 1.0.10
+ ws: 8.14.2
+ transitivePeerDependencies:
+ - bufferutil
+ - utf-8-validate
+ dev: false
+
+ /@supabase/ssr@0.0.10(@supabase/supabase-js@2.39.0):
+ resolution: {integrity: sha512-eVs7+bNlff8Fd79x8K3Jbfpmf8P8QRA1Z6rUDN+fi4ReWvRBZyWOFfR6eqlsX6vTjvGgTiEqujFSkv2PYW5kbQ==}
+ peerDependencies:
+ '@supabase/supabase-js': ^2.33.1
+ dependencies:
+ '@supabase/supabase-js': 2.39.0
+ cookie: 0.5.0
+ ramda: 0.29.0
+ dev: false
+
+ /@supabase/storage-js@2.5.5:
+ resolution: {integrity: sha512-OpLoDRjFwClwc2cjTJZG8XviTiQH4Ik8sCiMK5v7et0MDu2QlXjCAW3ljxJB5+z/KazdMOTnySi+hysxWUPu3w==}
+ dependencies:
+ '@supabase/node-fetch': 2.6.15
+ dev: false
+
+ /@supabase/supabase-js@2.39.0:
+ resolution: {integrity: sha512-cYfnwWRW5rYBbPT/BNIejtRT9ULdD9PnIExQV28PZpqcqm3PLwS4f3pY7WGB01Da63VYdvktZPKuYvreqsj/Zg==}
+ dependencies:
+ '@supabase/functions-js': 2.1.5
+ '@supabase/gotrue-js': 2.60.1
+ '@supabase/node-fetch': 2.6.15
+ '@supabase/postgrest-js': 1.9.0
+ '@supabase/realtime-js': 2.9.0
+ '@supabase/storage-js': 2.5.5
+ transitivePeerDependencies:
+ - bufferutil
+ - utf-8-validate
+ dev: false
+
/@swc/core-darwin-arm64@1.3.100:
resolution: {integrity: sha512-XVWFsKe6ei+SsDbwmsuRkYck1SXRpO60Hioa4hoLwR8fxbA9eVp6enZtMxzVVMBi8ej5seZ4HZQeAWepbukiBw==}
engines: {node: '>=10'}
@@ -4811,7 +4884,6 @@ packages:
resolution: {integrity: sha512-D08YG6rr8X90YB56tSIuBaddy/UXAA9RKJoFvrsnogAum/0pmjkgi4+2nx96A330FmioegBWmEYQ+syqCFaveg==}
dependencies:
undici-types: 5.26.5
- dev: true
/@types/normalize-package-data@2.4.4:
resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==}
@@ -4821,6 +4893,10 @@ packages:
resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==}
dev: true
+ /@types/phoenix@1.6.4:
+ resolution: {integrity: sha512-B34A7uot1Cv0XtaHRYDATltAdKx0BvVKNgYNqE4WjtPUa4VQJM7kxeXcVKaH+KS+kCmZ+6w+QaUdcljiheiBJA==}
+ dev: false
+
/@types/pretty-hrtime@1.0.3:
resolution: {integrity: sha512-nj39q0wAIdhwn7DGUyT9irmsKK1tV0bd5WFEhgpqNTMFZ8cE+jieuTphCW0tfdm47S2zVT5mr09B28b1chmQMA==}
dev: true
@@ -4882,6 +4958,12 @@ packages:
resolution: {integrity: sha512-WUtIVRUZ9i5dYXefDEAI7sh9/O7jGvHg7Df/5O/gtH3Yabe5odI3UWopVR1qbPXQtvOxWu3mM4XxlYeZtMWF4g==}
dev: true
+ /@types/websocket@1.0.10:
+ resolution: {integrity: sha512-svjGZvPB7EzuYS94cI7a+qhwgGU1y89wUgjT6E2wVUfmAGIvRfT7obBvRtnhXCSsoMdlG4gBFGE7MfkIXZLoww==}
+ dependencies:
+ '@types/node': 20.10.4
+ dev: false
+
/@types/yargs-parser@21.0.3:
resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==}
dev: true
@@ -6401,7 +6483,6 @@ packages:
/cookie@0.5.0:
resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==}
engines: {node: '>= 0.6'}
- dev: true
/core-js-compat@3.34.0:
resolution: {integrity: sha512-4ZIyeNbW/Cn1wkMMDy+mvrRUxrwFNjKwbhCfQpDd+eLgYipDqp8oGFGtLmhh18EDPKA0g3VUBYOxQGGwvWLVpA==}
@@ -10662,7 +10743,6 @@ packages:
/ramda@0.29.0:
resolution: {integrity: sha512-BBea6L67bYLtdbOqfp8f58fPMqEwx0doL+pAi8TZyp2YWz8R9G8z9x75CZI8W+ftqhFHCpEX2cRnUUXK130iKA==}
- dev: true
/randombytes@2.1.0:
resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==}
@@ -11987,7 +12067,6 @@ packages:
/tr46@0.0.3:
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
- dev: true
/trim-newlines@3.0.1:
resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==}
@@ -12196,7 +12275,6 @@ packages:
/undici-types@5.26.5:
resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
- dev: true
/unicode-canonical-property-names-ecmascript@2.0.0:
resolution: {integrity: sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==}
@@ -12399,7 +12477,6 @@ packages:
/webidl-conversions@3.0.1:
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
- dev: true
/webpack-dev-middleware@6.1.1(webpack@5.89.0):
resolution: {integrity: sha512-y51HrHaFeeWir0YO4f0g+9GwZawuigzcAdRNon6jErXy/SqV/+O6eaVAzDqE6t3e3NpGeR5CS+cCDaTC+V3yEQ==}
@@ -12484,7 +12561,6 @@ packages:
dependencies:
tr46: 0.0.3
webidl-conversions: 3.0.1
- dev: true
/which-boxed-primitive@1.0.2:
resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
@@ -12618,7 +12694,6 @@ packages:
optional: true
utf-8-validate:
optional: true
- dev: true
/xtend@4.0.2:
resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
diff --git a/types/database.ts b/types/database.ts
new file mode 100644
index 0000000..07dbe5a
--- /dev/null
+++ b/types/database.ts
@@ -0,0 +1,348 @@
+export type Json =
+ | { [key: string]: Json | undefined }
+ | Json[]
+ | boolean
+ | null
+ | number
+ | string;
+
+export type Database = {
+ public: {
+ CompositeTypes: {
+ [_ in never]: never;
+ };
+ Enums: {
+ [_ in never]: never;
+ };
+ Functions: {
+ [_ in never]: never;
+ };
+ Tables: {
+ episode: {
+ Insert: {
+ audio_url: string;
+ created_at?: string;
+ description?: null | string;
+ duration: number;
+ episode_number: number;
+ id?: number;
+ images?: null | string[];
+ podcast_index_guid: string;
+ show: number;
+ size?: null | number;
+ title: string;
+ };
+ Relationships: [
+ {
+ columns: ['show'];
+ foreignKeyName: 'episode_show_fkey';
+ isOneToOne: false;
+ referencedColumns: ['id'];
+ referencedRelation: 'show';
+ },
+ ];
+ Row: {
+ audio_url: string;
+ created_at: string;
+ description: null | string;
+ duration: number;
+ episode_number: number;
+ id: number;
+ images: null | string[];
+ podcast_index_guid: string;
+ show: number;
+ size: null | number;
+ title: string;
+ };
+ Update: {
+ audio_url?: string;
+ created_at?: string;
+ description?: null | string;
+ duration?: number;
+ episode_number?: number;
+ id?: number;
+ images?: null | string[];
+ podcast_index_guid?: string;
+ show?: number;
+ size?: null | number;
+ title?: string;
+ };
+ };
+ episode_content: {
+ Insert: {
+ created_at?: string;
+ episode: number;
+ id?: number;
+ text_summary?: null | string;
+ transcription?: null | string;
+ user: number;
+ };
+ Relationships: [
+ {
+ columns: ['episode'];
+ foreignKeyName: 'episode_content_episode_fkey';
+ isOneToOne: false;
+ referencedColumns: ['id'];
+ referencedRelation: 'episode';
+ },
+ {
+ columns: ['user'];
+ foreignKeyName: 'episode_content_user_fkey';
+ isOneToOne: false;
+ referencedColumns: ['id'];
+ referencedRelation: 'user';
+ },
+ ];
+ Row: {
+ created_at: string;
+ episode: number;
+ id: number;
+ text_summary: null | string;
+ transcription: null | string;
+ user: number;
+ };
+ Update: {
+ created_at?: string;
+ episode?: number;
+ id?: number;
+ text_summary?: null | string;
+ transcription?: null | string;
+ user?: number;
+ };
+ };
+ episode_progress: {
+ Insert: {
+ at: number;
+ created_at?: string;
+ episode: number;
+ finished?: boolean;
+ id?: number;
+ user: number;
+ };
+ Relationships: [
+ {
+ columns: ['episode'];
+ foreignKeyName: 'episode_progress_episode_fkey';
+ isOneToOne: false;
+ referencedColumns: ['id'];
+ referencedRelation: 'episode';
+ },
+ {
+ columns: ['user'];
+ foreignKeyName: 'episode_progress_user_fkey';
+ isOneToOne: false;
+ referencedColumns: ['id'];
+ referencedRelation: 'user';
+ },
+ ];
+ Row: {
+ at: number;
+ created_at: string;
+ episode: number;
+ finished: boolean;
+ id: number;
+ user: number;
+ };
+ Update: {
+ at?: number;
+ created_at?: string;
+ episode?: number;
+ finished?: boolean;
+ id?: number;
+ user?: number;
+ };
+ };
+ show: {
+ Insert: {
+ created_at?: string;
+ description?: null | string;
+ id?: number;
+ images?: null | string[];
+ language?: null | string;
+ podcast_index_guid: string;
+ publisher: string;
+ spotify_id: string;
+ title: string;
+ total_episode?: null | number;
+ };
+ Relationships: [];
+ Row: {
+ created_at: string;
+ description: null | string;
+ id: number;
+ images: null | string[];
+ language: null | string;
+ podcast_index_guid: string;
+ publisher: string;
+ spotify_id: string;
+ title: string;
+ total_episode: null | number;
+ };
+ Update: {
+ created_at?: string;
+ description?: null | string;
+ id?: number;
+ images?: null | string[];
+ language?: null | string;
+ podcast_index_guid?: string;
+ publisher?: string;
+ spotify_id?: string;
+ title?: string;
+ total_episode?: null | number;
+ };
+ };
+ user: {
+ Insert: {
+ ai_credit?: null | number;
+ avatar?: null | string;
+ created_at?: string;
+ email?: null | string;
+ id?: number;
+ password?: null | string;
+ provider_token?: null | string;
+ username?: null | string;
+ };
+ Relationships: [];
+ Row: {
+ ai_credit: null | number;
+ avatar: null | string;
+ created_at: string;
+ email: null | string;
+ id: number;
+ password: null | string;
+ provider_token: null | string;
+ username: null | string;
+ };
+ Update: {
+ ai_credit?: null | number;
+ avatar?: null | string;
+ created_at?: string;
+ email?: null | string;
+ id?: number;
+ password?: null | string;
+ provider_token?: null | string;
+ username?: null | string;
+ };
+ };
+ user_show_relation: {
+ Insert: {
+ created_at?: string;
+ id?: number;
+ show: number;
+ user: number;
+ };
+ Relationships: [
+ {
+ columns: ['show'];
+ foreignKeyName: 'user_show_relation_show_fkey';
+ isOneToOne: false;
+ referencedColumns: ['id'];
+ referencedRelation: 'show';
+ },
+ {
+ columns: ['user'];
+ foreignKeyName: 'user_show_relation_user_fkey';
+ isOneToOne: false;
+ referencedColumns: ['id'];
+ referencedRelation: 'user';
+ },
+ ];
+ Row: {
+ created_at: string;
+ id: number;
+ show: number;
+ user: number;
+ };
+ Update: {
+ created_at?: string;
+ id?: number;
+ show?: number;
+ user?: number;
+ };
+ };
+ };
+ Views: {
+ [_ in never]: never;
+ };
+ };
+};
+
+export type Tables<
+ PublicTableNameOrOptions extends
+ | { schema: keyof Database }
+ | keyof (Database['public']['Tables'] & Database['public']['Views']),
+ TableName extends PublicTableNameOrOptions extends { schema: keyof Database }
+ ? keyof (Database[PublicTableNameOrOptions['schema']]['Tables'] &
+ Database[PublicTableNameOrOptions['schema']]['Views'])
+ : never = never,
+> = PublicTableNameOrOptions extends { schema: keyof Database }
+ ? (Database[PublicTableNameOrOptions['schema']]['Tables'] &
+ Database[PublicTableNameOrOptions['schema']]['Views'])[TableName] extends {
+ Row: infer R;
+ }
+ ? R
+ : never
+ : PublicTableNameOrOptions extends keyof (Database['public']['Tables'] &
+ Database['public']['Views'])
+ ? (Database['public']['Tables'] &
+ Database['public']['Views'])[PublicTableNameOrOptions] extends {
+ Row: infer R;
+ }
+ ? R
+ : never
+ : never;
+
+export type TablesInsert<
+ PublicTableNameOrOptions extends
+ | { schema: keyof Database }
+ | keyof Database['public']['Tables'],
+ TableName extends PublicTableNameOrOptions extends { schema: keyof Database }
+ ? keyof Database[PublicTableNameOrOptions['schema']]['Tables']
+ : never = never,
+> = PublicTableNameOrOptions extends { schema: keyof Database }
+ ? Database[PublicTableNameOrOptions['schema']]['Tables'][TableName] extends {
+ Insert: infer I;
+ }
+ ? I
+ : never
+ : PublicTableNameOrOptions extends keyof Database['public']['Tables']
+ ? Database['public']['Tables'][PublicTableNameOrOptions] extends {
+ Insert: infer I;
+ }
+ ? I
+ : never
+ : never;
+
+export type TablesUpdate<
+ PublicTableNameOrOptions extends
+ | { schema: keyof Database }
+ | keyof Database['public']['Tables'],
+ TableName extends PublicTableNameOrOptions extends { schema: keyof Database }
+ ? keyof Database[PublicTableNameOrOptions['schema']]['Tables']
+ : never = never,
+> = PublicTableNameOrOptions extends { schema: keyof Database }
+ ? Database[PublicTableNameOrOptions['schema']]['Tables'][TableName] extends {
+ Update: infer U;
+ }
+ ? U
+ : never
+ : PublicTableNameOrOptions extends keyof Database['public']['Tables']
+ ? Database['public']['Tables'][PublicTableNameOrOptions] extends {
+ Update: infer U;
+ }
+ ? U
+ : never
+ : never;
+
+export type Enums<
+ PublicEnumNameOrOptions extends
+ | { schema: keyof Database }
+ | keyof Database['public']['Enums'],
+ EnumName extends PublicEnumNameOrOptions extends { schema: keyof Database }
+ ? keyof Database[PublicEnumNameOrOptions['schema']]['Enums']
+ : never = never,
+> = PublicEnumNameOrOptions extends { schema: keyof Database }
+ ? Database[PublicEnumNameOrOptions['schema']]['Enums'][EnumName]
+ : PublicEnumNameOrOptions extends keyof Database['public']['Enums']
+ ? Database['public']['Enums'][PublicEnumNameOrOptions]
+ : never;