mirror of
https://github.com/stackblitz-labs/bolt.diy
synced 2025-06-26 18:26:38 +00:00
Merge 9b7940a630
into 05d7ef0ab5
This commit is contained in:
commit
df78aa0f34
@ -44,11 +44,26 @@ export function SupabaseChatAlert({ alert, clearAlert, postMessage }: Props) {
|
||||
setIsExecuting(true);
|
||||
|
||||
try {
|
||||
// Use authenticated user's access token if available
|
||||
let accessToken = connection.token;
|
||||
if (connection.credentials?.supabaseUrl && connection.credentials?.anonKey) {
|
||||
try {
|
||||
const { getSupabaseClient } = require("~/lib/supabaseClient");
|
||||
const supabase = getSupabaseClient(connection.credentials.supabaseUrl, connection.credentials.anonKey);
|
||||
const session = await supabase.auth.getSession();
|
||||
if (session.data?.session?.access_token) {
|
||||
accessToken = session.data.session.access_token;
|
||||
}
|
||||
} catch (e) {
|
||||
// fallback to connection.token
|
||||
}
|
||||
}
|
||||
|
||||
const response = await fetch('/api/supabase/query', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${connection.token}`,
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
projectId: connection.selectedProjectId,
|
||||
|
136
app/components/chat/SupabaseAuth.tsx
Normal file
136
app/components/chat/SupabaseAuth.tsx
Normal file
@ -0,0 +1,136 @@
|
||||
import React, { useState } from "react";
|
||||
import { getSupabaseClient } from "~/lib/supabaseClient";
|
||||
|
||||
interface SupabaseAuthProps {
|
||||
supabaseUrl: string;
|
||||
supabaseAnonKey: string;
|
||||
}
|
||||
|
||||
export const SupabaseAuth: React.FC<SupabaseAuthProps> = ({
|
||||
supabaseUrl,
|
||||
supabaseAnonKey,
|
||||
}) => {
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [authState, setAuthState] = useState<"idle" | "loading" | "signedIn" | "signedOut" | "needsConfirmation" | "error">("idle");
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [user, setUser] = useState<any>(null);
|
||||
|
||||
const supabase = getSupabaseClient(supabaseUrl, supabaseAnonKey);
|
||||
|
||||
const handleSignUp = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setAuthState("loading");
|
||||
setError(null);
|
||||
const { data, error } = await supabase.auth.signUp({
|
||||
email,
|
||||
password,
|
||||
});
|
||||
if (error) {
|
||||
setAuthState("error");
|
||||
setError(error.message);
|
||||
} else if (data.user && !data.user.confirmed_at) {
|
||||
setAuthState("needsConfirmation");
|
||||
setUser(data.user);
|
||||
} else if (data.user) {
|
||||
setAuthState("signedIn");
|
||||
setUser(data.user);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSignIn = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setAuthState("loading");
|
||||
setError(null);
|
||||
const { data, error } = await supabase.auth.signInWithPassword({
|
||||
email,
|
||||
password,
|
||||
});
|
||||
if (error) {
|
||||
setAuthState("error");
|
||||
setError(error.message);
|
||||
} else if (data.user) {
|
||||
setAuthState("signedIn");
|
||||
setUser(data.user);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSignOut = async () => {
|
||||
setAuthState("loading");
|
||||
setError(null);
|
||||
const { error } = await supabase.auth.signOut();
|
||||
if (error) {
|
||||
setAuthState("error");
|
||||
setError(error.message);
|
||||
} else {
|
||||
setAuthState("signedOut");
|
||||
setUser(null);
|
||||
}
|
||||
};
|
||||
|
||||
const handleResendConfirmation = async () => {
|
||||
setAuthState("loading");
|
||||
setError(null);
|
||||
const { error } = await supabase.auth.resend({
|
||||
type: "signup",
|
||||
email,
|
||||
} as any);
|
||||
if (error) {
|
||||
setAuthState("error");
|
||||
setError(error.message);
|
||||
} else {
|
||||
setAuthState("needsConfirmation");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="supabase-auth">
|
||||
{authState === "signedIn" && user ? (
|
||||
<div>
|
||||
<p>Signed in as {user.email}</p>
|
||||
<button onClick={handleSignOut}>Sign Out</button>
|
||||
</div>
|
||||
) : (
|
||||
<form onSubmit={authState === "needsConfirmation" ? undefined : handleSignIn}>
|
||||
<input
|
||||
type="email"
|
||||
placeholder="Email"
|
||||
value={email}
|
||||
onChange={e => setEmail(e.target.value)}
|
||||
required
|
||||
autoComplete="email"
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
value={password}
|
||||
onChange={e => setPassword(e.target.value)}
|
||||
required
|
||||
autoComplete="current-password"
|
||||
/>
|
||||
<div style={{ display: "flex", gap: "8px" }}>
|
||||
<button type="button" onClick={handleSignUp} disabled={authState === "loading"}>
|
||||
Sign Up
|
||||
</button>
|
||||
<button type="submit" disabled={authState === "loading"}>
|
||||
Sign In
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
)}
|
||||
{authState === "needsConfirmation" && (
|
||||
<div>
|
||||
<p>
|
||||
Please check your email to confirm your account. After confirming, you can sign in.
|
||||
</p>
|
||||
<button onClick={handleResendConfirmation} disabled={authState === "loading"}>
|
||||
Resend Confirmation Email
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{error && <div style={{ color: "red" }}>{error}</div>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SupabaseAuth;
|
@ -288,6 +288,19 @@ export function SupabaseConnection() {
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Supabase Auth UI */}
|
||||
{supabaseConn.credentials?.supabaseUrl && supabaseConn.credentials?.anonKey && (
|
||||
<div className="mt-6">
|
||||
{/* Dynamically import to avoid SSR issues if needed */}
|
||||
{typeof window !== "undefined" && (
|
||||
<>{require("./SupabaseAuth").SupabaseAuth({
|
||||
supabaseUrl: supabaseConn.credentials.supabaseUrl,
|
||||
supabaseAnonKey: supabaseConn.credentials.anonKey,
|
||||
})}</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
14
app/lib/supabaseClient.ts
Normal file
14
app/lib/supabaseClient.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { createClient, SupabaseClient } from '@supabase/supabase-js';
|
||||
|
||||
let supabase: SupabaseClient | null = null;
|
||||
let lastUrl: string | null = null;
|
||||
let lastAnonKey: string | null = null;
|
||||
|
||||
export function getSupabaseClient(supabaseUrl: string, supabaseAnonKey: string): SupabaseClient {
|
||||
if (!supabase || lastUrl !== supabaseUrl || lastAnonKey !== supabaseAnonKey) {
|
||||
supabase = createClient(supabaseUrl, supabaseAnonKey);
|
||||
lastUrl = supabaseUrl;
|
||||
lastAnonKey = supabaseAnonKey;
|
||||
}
|
||||
return supabase;
|
||||
}
|
33439
package-lock.json
generated
Normal file
33439
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -93,6 +93,7 @@
|
||||
"@remix-run/cloudflare-pages": "^2.15.2",
|
||||
"@remix-run/node": "^2.15.2",
|
||||
"@remix-run/react": "^2.15.2",
|
||||
"@supabase/supabase-js": "^2.49.4",
|
||||
"@tanstack/react-virtual": "^3.13.0",
|
||||
"@types/react-beautiful-dnd": "^13.1.8",
|
||||
"@uiw/codemirror-theme-vscode": "^4.23.6",
|
||||
|
Loading…
Reference in New Issue
Block a user