Supabase & Astro
Supabase 是一个开源的 Firebase 替代品。它提供 Postgres 数据库、身份验证、边缘函数、实时订阅和存储功能。
在 Astro 中初始化 Supabase
标题为“在 Astro 中初始化 Supabase”的部分先决条件
标题为“先决条件”的部分- 一个 Supabase 项目。如果你还没有,可以在 supabase.com 免费注册并创建一个新项目。
- 一个启用了按需渲染的
output: 'server'
的 Astro 项目。 - 你的项目的 Supabase 凭据。你可以在 Supabase 项目的 Settings > API 选项卡中找到它们。
SUPABASE_URL
:你的 Supabase 项目的 URL。SUPABASE_ANON_KEY
:你的 Supabase 项目的匿名密钥。
添加 Supabase 凭据
标题为“添加 Supabase 凭据”的部分要将你的 Supabase 凭据添加到 Astro 项目中,请将以下内容添加到你的 .env
文件中
SUPABASE_URL=YOUR_SUPABASE_URLSUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY
现在,这些环境变量就可以在你的项目中使用。
如果你希望为你的环境变量提供智能提示(IntelliSense),请在 src/
目录下编辑或创建 env.d.ts
文件,并添加以下内容
interface ImportMetaEnv { readonly SUPABASE_URL: string readonly SUPABASE_ANON_KEY: string}
interface ImportMeta { readonly env: ImportMetaEnv}
在 Astro 中阅读更多关于环境变量和 .env
文件的信息。
你的项目现在应包含以下文件
目录src/
- env.d.ts
- .env
- astro.config.mjs
- package.json
安装依赖
标题为“安装依赖”的部分要连接到 Supabase,你需要在项目中安装 @supabase/supabase-js
。
npm install @supabase/supabase-js
pnpm add @supabase/supabase-js
yarn add @supabase/supabase-js
接下来,在你的 src/
目录下创建一个名为 lib
的文件夹。你将在这里添加你的 Supabase 客户端。
在 supabase.ts
中,添加以下内容来初始化你的 Supabase 客户端
import { createClient } from "@supabase/supabase-js";
export const supabase = createClient( import.meta.env.SUPABASE_URL, import.meta.env.SUPABASE_ANON_KEY,);
现在,你的项目应包含以下文件
目录src/
目录lib/
- supabase.ts
- env.d.ts
- .env
- astro.config.mjs
- package.json
使用 Supabase 添加身份验证
标题为“使用 Supabase 添加身份验证”的部分Supabase 提供开箱即用的身份验证功能。它支持电子邮件/密码认证和多种提供商的 OAuth 认证,包括 GitHub、Google 等。
先决条件
标题为“先决条件”的部分- 一个用 Supabase 初始化的 Astro 项目。
- 一个启用了电子邮件/密码认证的 Supabase 项目。你可以在 Supabase 项目的 Authentication > Providers 选项卡中启用此功能。
创建认证服务器端点
标题为“创建认证服务器端点”的部分要为你的项目添加身份验证,你需要创建几个服务器端点。这些端点将用于注册、登录和注销用户。
POST /api/auth/register
:注册一个新用户。POST /api/auth/signin
:登录一个用户。GET /api/auth/signout
:注销一个用户。
在项目的 src/pages/api/auth
目录下创建这些端点。如果你使用 static
渲染模式,则必须在每个文件的顶部指定 export const prerender = false
以按需渲染这些端点。你的项目现在应包含以下新文件:
目录src/
目录lib/
- supabase.ts
目录pages/
目录api/
目录auth/
- signin.ts
- signout.ts
- register.ts
- env.d.ts
- .env
- astro.config.mjs
- package.json
register.ts
在 Supabase 中创建一个新用户。它接受一个带有电子邮件和密码的 POST
请求,然后使用 Supabase SDK 创建一个新用户。
// With `output: 'static'` configured:// export const prerender = false;import type { APIRoute } from "astro";import { supabase } from "../../../lib/supabase";
export const POST: APIRoute = async ({ request, redirect }) => { const formData = await request.formData(); const email = formData.get("email")?.toString(); const password = formData.get("password")?.toString();
if (!email || !password) { return new Response("Email and password are required", { status: 400 }); }
const { error } = await supabase.auth.signUp({ email, password, });
if (error) { return new Response(error.message, { status: 500 }); }
return redirect("/signin");};
signin.ts
用于登录用户。它接受一个带有电子邮件和密码的 POST
请求,然后使用 Supabase SDK 登录用户。
// With `output: 'static'` configured:// export const prerender = false;import type { APIRoute } from "astro";import { supabase } from "../../../lib/supabase";
export const POST: APIRoute = async ({ request, cookies, redirect }) => { const formData = await request.formData(); const email = formData.get("email")?.toString(); const password = formData.get("password")?.toString();
if (!email || !password) { return new Response("Email and password are required", { status: 400 }); }
const { data, error } = await supabase.auth.signInWithPassword({ email, password, });
if (error) { return new Response(error.message, { status: 500 }); }
const { access_token, refresh_token } = data.session; cookies.set("sb-access-token", access_token, { path: "/", }); cookies.set("sb-refresh-token", refresh_token, { path: "/", }); return redirect("/dashboard");};
signout.ts
用于注销用户。它接受一个 GET
请求,并移除用户的访问令牌和刷新令牌。
// With `output: 'static'` configured:// export const prerender = false;import type { APIRoute } from "astro";
export const GET: APIRoute = async ({ cookies, redirect }) => { cookies.delete("sb-access-token", { path: "/" }); cookies.delete("sb-refresh-token", { path: "/" }); return redirect("/signin");};
创建认证页面
标题为“创建认证页面”的部分现在你已经创建了服务器端点,接着创建将使用它们的页面。
src/pages/register
:包含一个用于注册新用户的表单。src/pages/signin
:包含一个用于登录用户的表单。src/pages/dashboard
:包含一个只有经过身份验证的用户才能访问的页面。
在 src/pages
目录下创建这些页面。你的项目现在应包含以下新文件:
目录src/
目录lib/
- supabase.ts
目录pages/
目录api/
目录auth/
- signin.ts
- signout.ts
- register.ts
- register.astro
- signin.astro
- dashboard.astro
- env.d.ts
- .env
- astro.config.mjs
- package.json
register.astro
包含一个用于注册新用户的表单。它接受电子邮件和密码,并向 /api/auth/register
发送 POST
请求。
---import Layout from "../layouts/Layout.astro";---
<Layout title="Register"> <h1>Register</h1> <p>Already have an account? <a href="/signin">Sign in</a></p> <form action="/api/auth/register" method="post"> <label for="email">Email</label> <input type="email" name="email" id="email" /> <label for="password">Password</label> <input type="password" name="password" id="password" /> <button type="submit">Register</button> </form></Layout>
signin.astro
包含一个用于登录用户的表单。它接受电子邮件和密码,并向 /api/auth/signin
发送 POST
请求。它还会检查访问令牌和刷新令牌是否存在。如果存在,它将重定向到仪表盘。
---import Layout from "../layouts/Layout.astro";
const { cookies, redirect } = Astro;
const accessToken = cookies.get("sb-access-token");const refreshToken = cookies.get("sb-refresh-token");
if (accessToken && refreshToken) { return redirect("/dashboard");}---
<Layout title="Sign in"> <h1>Sign in</h1> <p>New here? <a href="/register">Create an account</a></p> <form action="/api/auth/signin" method="post"> <label for="email">Email</label> <input type="email" name="email" id="email" /> <label for="password">Password</label> <input type="password" name="password" id="password" /> <button type="submit">Login</button> </form></Layout>
dashboard.astro
包含一个只有经过身份验证的用户才能访问的页面。它会检查访问令牌和刷新令牌是否存在。如果不存在或无效,它将重定向到登录页面。
---import Layout from "../layouts/Layout.astro";import { supabase } from "../lib/supabase";
const accessToken = Astro.cookies.get("sb-access-token");const refreshToken = Astro.cookies.get("sb-refresh-token");
if (!accessToken || !refreshToken) { return Astro.redirect("/signin");}
let session;try { session = await supabase.auth.setSession({ refresh_token: refreshToken.value, access_token: accessToken.value, }); if (session.error) { Astro.cookies.delete("sb-access-token", { path: "/", }); Astro.cookies.delete("sb-refresh-token", { path: "/", }); return Astro.redirect("/signin"); }} catch (error) { Astro.cookies.delete("sb-access-token", { path: "/", }); Astro.cookies.delete("sb-refresh-token", { path: "/", }); return Astro.redirect("/signin");}
const email = session.data.user?.email;---<Layout title="dashboard"> <h1>Welcome {email}</h1> <p>We are happy to see you here</p> <form action="/api/auth/signout"> <button type="submit">Sign out</button> </form></Layout>
添加 OAuth 身份验证
标题为“添加 OAuth 身份验证”的部分要为你的项目添加 OAuth 身份验证,你需要编辑你的 Supabase 客户端以启用 "pkce"
身份验证流程。你可以在 Supabase 文档中阅读更多关于身份验证流程的信息。
import { createClient } from "@supabase/supabase-js";
export const supabase = createClient( import.meta.env.SUPABASE_URL, import.meta.env.SUPABASE_ANON_KEY, { auth: { flowType: "pkce", }, },);
接下来,在 Supabase 仪表盘中,启用你想要使用的 OAuth 提供商。你可以在 Supabase 项目的 Authentication > Providers 选项卡中找到支持的提供商列表。
以下示例使用 GitHub 作为 OAuth 提供商。要将你的项目连接到 GitHub,请按照 Supabase 文档中的步骤操作。
然后,在 src/pages/api/auth/callback.ts
创建一个新的服务器端点来处理 OAuth 回调。此端点将用于将 OAuth 授权码交换为访问令牌和刷新令牌。
import type { APIRoute } from "astro";import { supabase } from "../../../lib/supabase";
export const GET: APIRoute = async ({ url, cookies, redirect }) => { const authCode = url.searchParams.get("code");
if (!authCode) { return new Response("No code provided", { status: 400 }); }
const { data, error } = await supabase.auth.exchangeCodeForSession(authCode);
if (error) { return new Response(error.message, { status: 500 }); }
const { access_token, refresh_token } = data.session;
cookies.set("sb-access-token", access_token, { path: "/", }); cookies.set("sb-refresh-token", refresh_token, { path: "/", });
return redirect("/dashboard");};
接下来,编辑登录页面,添加一个新按钮以使用 OAuth 提供商登录。此按钮应向 /api/auth/signin
发送一个 POST
请求,并将 provider
设置为 OAuth 提供商的名称。
---import Layout from "../layouts/Layout.astro";
const { cookies, redirect } = Astro;
const accessToken = cookies.get("sb-access-token");const refreshToken = cookies.get("sb-refresh-token");
if (accessToken && refreshToken) { return redirect("/dashboard");}---
<Layout title="Sign in"> <h1>Sign in</h1> <p>New here? <a href="/register">Create an account</a></p> <form action="/api/auth/signin" method="post"> <label for="email">Email</label> <input type="email" name="email" id="email" /> <label for="password">Password</label> <input type="password" name="password" id="password" /> <button type="submit">Login</button> <button value="github" name="provider" type="submit">Sign in with GitHub</button> </form></Layout>
最后,编辑登录服务器端点以处理 OAuth 提供商。如果存在 provider
,它将重定向到 OAuth 提供商。否则,它将使用电子邮件和密码登录用户。
import type { APIRoute } from "astro";import { supabase } from "../../../lib/supabase";import type { Provider } from "@supabase/supabase-js";
export const POST: APIRoute = async ({ request, cookies, redirect }) => { const formData = await request.formData(); const email = formData.get("email")?.toString(); const password = formData.get("password")?.toString(); const provider = formData.get("provider")?.toString();
const validProviders = ["google", "github", "discord"];
if (provider && validProviders.includes(provider)) { const { data, error } = await supabase.auth.signInWithOAuth({ provider: provider as Provider, options: { redirectTo: "https://:4321/api/auth/callback" }, });
if (error) { return new Response(error.message, { status: 500 }); }
return redirect(data.url); }
if (!email || !password) { return new Response("Email and password are required", { status: 400 }); }
const { data, error } = await supabase.auth.signInWithPassword({ email, password, });
if (error) { return new Response(error.message, { status: 500 }); }
const { access_token, refresh_token } = data.session; cookies.set("sb-access-token", access_token, { path: "/", }); cookies.set("sb-refresh-token", refresh_token, { path: "/", }); return redirect("/dashboard");};
在创建 OAuth 回调端点并编辑登录页面和服务器端点后,你的项目应具有以下文件结构
目录src/
目录lib/
- supabase.ts
目录pages/
目录api/
目录auth/
- signin.ts
- signout.ts
- register.ts
- callback.ts
- register.astro
- signin.astro
- dashboard.astro
- env.d.ts
- .env
- astro.config.mjs
- package.json