mirror of
https://github.com/supabase/supabase.git
synced 2026-10-08 10:55:06 +03:00
This PR bumps `shadcn` and regenerates all blocks with the latest CLI. The blocks have no meaningful change (only a json property reorder). <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Updated internal development tooling used by the UI library and Vue blocks. * **Tests** * Improved type consistency in registry-related test utilities, supporting more reliable validation without changing user-facing behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
73 lines
9.9 KiB
JSON
73 lines
9.9 KiB
JSON
{
|
|
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
"name": "social-auth-tanstack",
|
|
"title": "Social Auth flow for TanStack and Supabase",
|
|
"description": "Social Auth flow for TanStack and Supabase",
|
|
"dependencies": [
|
|
"@supabase/ssr@latest",
|
|
"@supabase/supabase-js@latest"
|
|
],
|
|
"registryDependencies": [
|
|
"button",
|
|
"card",
|
|
"https://supabase.com/library/r/safe-next-path.json"
|
|
],
|
|
"files": [
|
|
{
|
|
"path": "registry/default/blocks/social-auth-tanstack/components/login-form.tsx",
|
|
"content": "import { useState } from 'react'\n\nimport { cn } from '@/lib/utils'\nimport { safeNextPath } from '@/registry/default/blocks/safe-next-path/lib/safe-next-path'\nimport { createClient } from '@/registry/default/clients/tanstack/lib/supabase/client'\nimport { Button } from '@/registry/default/components/ui/button'\nimport {\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n} from '@/registry/default/components/ui/card'\n\nexport function LoginForm({ className, ...props }: React.ComponentPropsWithoutRef<'div'>) {\n const [error, setError] = useState<string | null>(null)\n const [isLoading, setIsLoading] = useState(false)\n\n const handleSocialLogin = async (e: React.FormEvent) => {\n e.preventDefault()\n const supabase = createClient()\n setIsLoading(true)\n setError(null)\n\n try {\n const next = new URLSearchParams(window.location.search).get('next')\n const { error } = await supabase.auth.signInWithOAuth({\n provider: 'github',\n options: {\n redirectTo: `${window.location.origin}/auth/confirm?next=${encodeURIComponent(safeNextPath(next, '/protected'))}`,\n },\n })\n\n if (error) throw error\n } catch (error: unknown) {\n setError(error instanceof Error ? error.message : 'An error occurred')\n setIsLoading(false)\n }\n }\n\n return (\n <div className={cn('flex flex-col gap-6', className)} {...props}>\n <Card>\n <CardHeader>\n <CardTitle className=\"text-2xl\">Welcome!</CardTitle>\n <CardDescription>Sign in to your account to continue</CardDescription>\n </CardHeader>\n <CardContent>\n <form onSubmit={handleSocialLogin}>\n <div className=\"flex flex-col gap-6\">\n {error && <p className=\"text-sm text-destructive-500\">{error}</p>}\n <Button type=\"submit\" className=\"w-full\" disabled={isLoading}>\n {isLoading ? 'Signing in...' : 'Continue with GitHub'}\n </Button>\n </div>\n </form>\n </CardContent>\n </Card>\n </div>\n )\n}\n",
|
|
"type": "registry:component"
|
|
},
|
|
{
|
|
"path": "registry/default/blocks/social-auth-tanstack/lib/supabase/fetch-user-server-fn.ts",
|
|
"content": "import type { Factor, User } from '@supabase/supabase-js'\nimport { createServerFn } from '@tanstack/react-start'\n\nimport { createClient } from '@/registry/default/clients/tanstack/lib/supabase/server'\n\ntype SSRSafeUser = User & {\n factors: (Factor & { factor_type: 'phone' | 'totp' })[]\n}\n\nexport const fetchUser: () => Promise<SSRSafeUser | null> = createServerFn({\n method: 'GET',\n}).handler(async () => {\n const supabase = createClient()\n const { data, error } = await supabase.auth.getUser()\n\n if (error) {\n return null\n }\n\n return data.user as SSRSafeUser\n})\n",
|
|
"type": "registry:lib"
|
|
},
|
|
{
|
|
"path": "registry/default/blocks/social-auth-tanstack/routes/_protected.tsx",
|
|
"content": "import { createFileRoute, redirect } from '@tanstack/react-router'\n\nimport { fetchUser } from '@/registry/default/blocks/social-auth-tanstack/lib/supabase/fetch-user-server-fn'\n\nexport const Route = createFileRoute('/_protected')({\n beforeLoad: async () => {\n const user = await fetchUser()\n\n if (!user) {\n throw redirect({ to: '/login' })\n }\n\n return {\n user,\n }\n },\n})\n",
|
|
"type": "registry:file",
|
|
"target": "routes/_protected.tsx"
|
|
},
|
|
{
|
|
"path": "registry/default/blocks/social-auth-tanstack/routes/_protected/protected.tsx",
|
|
"content": "import { createFileRoute } from '@tanstack/react-router'\n\nexport const Route = createFileRoute('/_protected/info')({\n component: Info,\n loader: async ({ context }) => {\n return {\n user: context.user!,\n }\n },\n})\n\nfunction Info() {\n const data = Route.useLoaderData()\n\n return <p>Hello {data.user.email}</p>\n}\n",
|
|
"type": "registry:file",
|
|
"target": "routes/_protected/protected.tsx"
|
|
},
|
|
{
|
|
"path": "registry/default/blocks/social-auth-tanstack/routes/auth/error.tsx",
|
|
"content": "import { createFileRoute } from '@tanstack/react-router'\n\nimport { Card, CardContent, CardHeader, CardTitle } from '@/registry/default/components/ui/card'\n\nexport const Route = createFileRoute('/auth/error')({\n component: AuthError,\n validateSearch: (params) => {\n if (params.error && typeof params.error === 'string') {\n return { error: params.error }\n }\n return null\n },\n})\n\nfunction AuthError() {\n const params = Route.useSearch()\n\n return (\n <div className=\"flex min-h-svh w-full items-center justify-center p-6 md:p-10\">\n <div className=\"w-full max-w-sm\">\n <div className=\"flex flex-col gap-6\">\n <Card>\n <CardHeader>\n <CardTitle className=\"text-2xl\">Sorry, something went wrong.</CardTitle>\n </CardHeader>\n <CardContent>\n {params?.error ? (\n <p className=\"text-sm text-muted-foreground\">Code error: {params.error}</p>\n ) : (\n <p className=\"text-sm text-muted-foreground\">An unspecified error occurred.</p>\n )}\n </CardContent>\n </Card>\n </div>\n </div>\n </div>\n )\n}\n",
|
|
"type": "registry:file",
|
|
"target": "routes/auth/error.tsx"
|
|
},
|
|
{
|
|
"path": "registry/default/blocks/social-auth-tanstack/routes/auth/oauth.ts",
|
|
"content": "import { createFileRoute, redirect } from '@tanstack/react-router'\nimport { createServerFn } from '@tanstack/react-start'\nimport { getRequest } from '@tanstack/react-start/server'\n\nimport { createClient } from '@/registry/default/clients/tanstack/lib/supabase/server'\n\nconst confirmFn = createServerFn({ method: 'GET' })\n .inputValidator((searchParams: unknown) => {\n if (\n searchParams &&\n typeof searchParams === 'object' &&\n 'code' in searchParams &&\n 'next' in searchParams\n ) {\n return searchParams\n }\n throw new Error('Invalid search params')\n })\n .handler(async (ctx) => {\n const request = getRequest()\n\n if (!request) {\n throw redirect({ to: `/auth/error`, search: { error: 'No request' } })\n }\n\n const searchParams = ctx.data\n const code = searchParams['code'] as string\n const _next = (searchParams['next'] ?? '/') as string\n const next = _next?.startsWith('/') ? _next : '/'\n\n if (code) {\n const supabase = createClient()\n\n const { error } = await supabase.auth.exchangeCodeForSession(code)\n if (!error) {\n // redirect user to specified redirect URL or root of app\n throw redirect({ href: next })\n } else {\n // redirect the user to an error page with some instructions\n throw redirect({\n to: `/auth/error`,\n search: { error: error?.message },\n })\n }\n }\n\n // redirect the user to an error page with some instructions\n throw redirect({\n to: `/auth/error`,\n search: { error: 'No code found' },\n })\n })\n\nexport const Route = createFileRoute('/auth/confirm')({\n preload: false,\n loader: (opts) => confirmFn({ data: opts.location.search }),\n})\n",
|
|
"type": "registry:file",
|
|
"target": "routes/auth/oauth.ts"
|
|
},
|
|
{
|
|
"path": "registry/default/blocks/social-auth-tanstack/routes/login.tsx",
|
|
"content": "import { createFileRoute } from '@tanstack/react-router'\n\nimport { LoginForm } from '@/registry/default/blocks/social-auth-tanstack/components/login-form'\n\nexport const Route = createFileRoute('/login')({\n component: Login,\n})\n\nfunction Login() {\n return (\n <div className=\"flex min-h-svh w-full items-center justify-center p-6 md:p-10\">\n <div className=\"w-full max-w-sm\">\n <LoginForm />\n </div>\n </div>\n )\n}\n",
|
|
"type": "registry:file",
|
|
"target": "routes/login.tsx"
|
|
},
|
|
{
|
|
"path": "registry/default/clients/tanstack/lib/supabase/client.ts",
|
|
"content": "/// <reference types=\"vite/types/importMeta.d.ts\" />\nimport { createBrowserClient } from '@supabase/ssr'\n\nexport function createClient() {\n return createBrowserClient(\n import.meta.env.VITE_SUPABASE_URL!,\n import.meta.env.VITE_SUPABASE_PUBLISHABLE_KEY!\n )\n}\n",
|
|
"type": "registry:lib"
|
|
},
|
|
{
|
|
"path": "registry/default/clients/tanstack/lib/supabase/server.ts",
|
|
"content": "import { createServerClient } from '@supabase/ssr'\nimport { getCookies, setCookie } from '@tanstack/react-start/server'\n\nexport function createClient() {\n return createServerClient(\n process.env.VITE_SUPABASE_URL!,\n process.env.VITE_SUPABASE_PUBLISHABLE_KEY!,\n {\n cookies: {\n getAll() {\n return Object.entries(getCookies()).map(\n ([name, value]) =>\n ({\n name,\n value,\n }) as { name: string; value: string }\n )\n },\n setAll(cookies) {\n cookies.forEach((cookie) => {\n setCookie(cookie.name, cookie.value)\n })\n },\n },\n }\n )\n}\n",
|
|
"type": "registry:lib"
|
|
}
|
|
],
|
|
"envVars": {
|
|
"VITE_SUPABASE_URL": "",
|
|
"VITE_SUPABASE_PUBLISHABLE_KEY": ""
|
|
},
|
|
"docs": "You'll need to set the following environment variables in your project: `VITE_SUPABASE_URL` and `VITE_SUPABASE_PUBLISHABLE_KEY`.",
|
|
"type": "registry:block"
|
|
} |