mirror of
https://github.com/stackblitz-labs/bolt.diy
synced 2025-06-23 02:16:08 +00:00
feat: add supabase and firebase setup wizard
This commit is contained in:
parent
407f60d74c
commit
ed83aebf32
@ -120,3 +120,12 @@ VITE_GITHUB_TOKEN_TYPE=classic
|
||||
# DEFAULT_NUM_CTX=12288 # Consumes 26GB of VRAM
|
||||
# DEFAULT_NUM_CTX=6144 # Consumes 24GB of VRAM
|
||||
DEFAULT_NUM_CTX=
|
||||
|
||||
# Supabase credentials
|
||||
VITE_SUPABASE_URL=
|
||||
VITE_SUPABASE_ANON_KEY=
|
||||
|
||||
# Firebase credentials
|
||||
VITE_FIREBASE_API_KEY=
|
||||
VITE_FIREBASE_AUTH_DOMAIN=
|
||||
VITE_FIREBASE_PROJECT_ID=
|
||||
|
@ -116,4 +116,13 @@ VITE_CLOUDFLARE_ACCOUNT_ID=
|
||||
# DEFAULT_NUM_CTX=24576 # Consumes 32GB of VRAM
|
||||
# DEFAULT_NUM_CTX=12288 # Consumes 26GB of VRAM
|
||||
# DEFAULT_NUM_CTX=6144 # Consumes 24GB of VRAM
|
||||
DEFAULT_NUM_CTX=
|
||||
DEFAULT_NUM_CTX=
|
||||
|
||||
# Supabase credentials
|
||||
VITE_SUPABASE_URL=
|
||||
VITE_SUPABASE_ANON_KEY=
|
||||
|
||||
# Firebase credentials
|
||||
VITE_FIREBASE_API_KEY=
|
||||
VITE_FIREBASE_AUTH_DOMAIN=
|
||||
VITE_FIREBASE_PROJECT_ID=
|
||||
|
@ -108,6 +108,7 @@ project, please check the [project management guide](./PROJECT.md) to get starte
|
||||
- **Download projects as ZIP** for easy portability Sync to a folder on the host.
|
||||
- **Integration-ready Docker support** for a hassle-free setup.
|
||||
- **Deploy** directly to **Netlify**, **Vercel**, or **Cloudflare Pages**
|
||||
- **Guided Setup Wizard** for Supabase and Firebase connections
|
||||
- **Import Figma designs** to generate starter UI code.
|
||||
|
||||
## Setup
|
||||
|
@ -14,6 +14,7 @@ import { toast } from 'react-toastify';
|
||||
import { extractTextFromFile } from '~/utils/fileExtract';
|
||||
import { SpeechRecognitionButton } from '~/components/chat/SpeechRecognition';
|
||||
import { SupabaseConnection } from './SupabaseConnection';
|
||||
import { SetupWizard } from '~/components/setup/SetupWizard';
|
||||
import { ExpoQrModal } from '~/components/workbench/ExpoQrModal';
|
||||
import styles from './BaseChat.module.scss';
|
||||
import type { ProviderInfo } from '~/types/model';
|
||||
@ -353,6 +354,7 @@ export const ChatBox: React.FC<ChatBoxProps> = (props) => {
|
||||
</div>
|
||||
) : null}
|
||||
<SupabaseConnection />
|
||||
<SetupWizard />
|
||||
<ExpoQrModal open={props.qrModalOpen} onClose={() => props.setQrModalOpen(false)} />
|
||||
</div>
|
||||
</div>
|
||||
|
135
app/components/setup/SetupWizard.tsx
Normal file
135
app/components/setup/SetupWizard.tsx
Normal file
@ -0,0 +1,135 @@
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { useState } from 'react';
|
||||
import { DialogRoot, Dialog, DialogTitle, DialogButton, DialogClose } from '~/components/ui/Dialog';
|
||||
import { Input } from '~/components/ui/Input';
|
||||
import { useSupabaseConnection } from '~/lib/hooks/useSupabaseConnection';
|
||||
import { firebaseConfig, updateFirebaseConfig } from '~/lib/stores/firebase';
|
||||
|
||||
export function SetupWizard() {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [provider, setProvider] = useState<'supabase' | 'firebase' | null>(null);
|
||||
const [step, setStep] = useState(1);
|
||||
|
||||
const firebase = useStore(firebaseConfig);
|
||||
|
||||
const {
|
||||
connection: supabaseConn,
|
||||
connecting,
|
||||
handleConnect,
|
||||
selectProject,
|
||||
fetchProjectApiKeys,
|
||||
updateToken,
|
||||
} = useSupabaseConnection();
|
||||
|
||||
const startWizard = () => {
|
||||
setProvider(null);
|
||||
setStep(1);
|
||||
setOpen(true);
|
||||
};
|
||||
|
||||
const handleFirebaseSave = () => {
|
||||
updateFirebaseConfig(firebase);
|
||||
setStep(3);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<button
|
||||
className="px-3 py-1.5 rounded-md text-xs bg-bolt-elements-item-backgroundDefault hover:bg-bolt-elements-item-backgroundActive"
|
||||
onClick={startWizard}
|
||||
>
|
||||
Setup Wizard
|
||||
</button>
|
||||
<DialogRoot open={open} onOpenChange={setOpen}>
|
||||
{open && (
|
||||
<Dialog className="max-w-[520px] p-6 space-y-4">
|
||||
{step === 1 && (
|
||||
<div className="space-y-4">
|
||||
<DialogTitle>Select a Provider</DialogTitle>
|
||||
<div className="flex gap-2">
|
||||
<DialogButton type="primary" onClick={() => { setProvider('supabase'); setStep(2); }}>
|
||||
Supabase
|
||||
</DialogButton>
|
||||
<DialogButton type="primary" onClick={() => { setProvider('firebase'); setStep(2); }}>
|
||||
Firebase
|
||||
</DialogButton>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{provider === 'supabase' && step === 2 && (
|
||||
<div className="space-y-4">
|
||||
<DialogTitle>Connect Supabase</DialogTitle>
|
||||
<Input
|
||||
placeholder="Supabase PAT"
|
||||
value={supabaseConn.token}
|
||||
onChange={(e) => updateToken(e.currentTarget.value)}
|
||||
/>
|
||||
<DialogButton type="primary" onClick={handleConnect} disabled={connecting}>
|
||||
{connecting ? 'Connecting...' : 'Connect'}
|
||||
</DialogButton>
|
||||
{supabaseConn.stats?.projects && (
|
||||
<div className="space-y-2 max-h-40 overflow-y-auto">
|
||||
{supabaseConn.stats.projects.map((p) => (
|
||||
<button
|
||||
key={p.id}
|
||||
className="block w-full text-left px-3 py-1 rounded hover:bg-bolt-elements-item-backgroundActive"
|
||||
onClick={() => {
|
||||
selectProject(p.id);
|
||||
fetchProjectApiKeys(p.id).catch(console.error);
|
||||
}}
|
||||
>
|
||||
{p.name}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{supabaseConn.credentials && (
|
||||
<div className="space-y-2 text-xs bg-bolt-elements-background-depth-3 p-3 rounded">
|
||||
<div>Add these to your <code>.env.local</code>:</div>
|
||||
<pre>{`VITE_SUPABASE_URL=${supabaseConn.credentials.supabaseUrl}\nVITE_SUPABASE_ANON_KEY=${supabaseConn.credentials.anonKey}`}</pre>
|
||||
<div className="mt-2">Sample code:</div>
|
||||
<pre>{`import { createClient } from '@supabase/supabase-js';\n\nexport const supabase = createClient(import.meta.env.VITE_SUPABASE_URL!, import.meta.env.VITE_SUPABASE_ANON_KEY!);`}</pre>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{provider === 'firebase' && step === 2 && (
|
||||
<div className="space-y-4">
|
||||
<DialogTitle>Firebase Configuration</DialogTitle>
|
||||
<Input
|
||||
placeholder="API Key"
|
||||
value={firebase.apiKey}
|
||||
onChange={(e) => updateFirebaseConfig({ apiKey: e.currentTarget.value })}
|
||||
/>
|
||||
<Input
|
||||
placeholder="Auth Domain"
|
||||
value={firebase.authDomain}
|
||||
onChange={(e) => updateFirebaseConfig({ authDomain: e.currentTarget.value })}
|
||||
/>
|
||||
<Input
|
||||
placeholder="Project ID"
|
||||
value={firebase.projectId}
|
||||
onChange={(e) => updateFirebaseConfig({ projectId: e.currentTarget.value })}
|
||||
/>
|
||||
<DialogButton type="primary" onClick={handleFirebaseSave}>Save</DialogButton>
|
||||
</div>
|
||||
)}
|
||||
{provider === 'firebase' && step === 3 && (
|
||||
<div className="space-y-2 text-xs bg-bolt-elements-background-depth-3 p-3 rounded">
|
||||
<div>Add these to your <code>.env.local</code>:</div>
|
||||
<pre>{`VITE_FIREBASE_API_KEY=${firebase.apiKey}\nVITE_FIREBASE_AUTH_DOMAIN=${firebase.authDomain}\nVITE_FIREBASE_PROJECT_ID=${firebase.projectId}`}</pre>
|
||||
<div className="mt-2">Sample code:</div>
|
||||
<pre>{`import { initializeApp } from 'firebase/app';\nimport { getAuth } from 'firebase/auth';\nimport { getFirestore } from 'firebase/firestore';\n\nconst firebaseConfig = {\n apiKey: import.meta.env.VITE_FIREBASE_API_KEY!,\n authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN!,\n projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID!,\n};\n\nconst app = initializeApp(firebaseConfig);\nexport const auth = getAuth(app);\nexport const db = getFirestore(app);`}</pre>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex justify-end gap-2 pt-2">
|
||||
<DialogClose asChild>
|
||||
<DialogButton type="secondary">Close</DialogButton>
|
||||
</DialogClose>
|
||||
</div>
|
||||
</Dialog>
|
||||
)}
|
||||
</DialogRoot>
|
||||
</div>
|
||||
);
|
||||
}
|
23
app/lib/stores/firebase.ts
Normal file
23
app/lib/stores/firebase.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { atom } from 'nanostores';
|
||||
|
||||
export interface FirebaseConfig {
|
||||
apiKey: string;
|
||||
authDomain: string;
|
||||
projectId: string;
|
||||
}
|
||||
|
||||
const saved = typeof localStorage !== 'undefined' ? localStorage.getItem('firebase_config') : null;
|
||||
|
||||
export const firebaseConfig = atom<FirebaseConfig>(
|
||||
saved ? JSON.parse(saved) : { apiKey: '', authDomain: '', projectId: '' },
|
||||
);
|
||||
|
||||
export function updateFirebaseConfig(config: Partial<FirebaseConfig>) {
|
||||
const current = firebaseConfig.get();
|
||||
const newConfig = { ...current, ...config };
|
||||
firebaseConfig.set(newConfig);
|
||||
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
localStorage.setItem('firebase_config', JSON.stringify(newConfig));
|
||||
}
|
||||
}
|
5
app/types/firebase.ts
Normal file
5
app/types/firebase.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export interface FirebaseConfig {
|
||||
apiKey: string;
|
||||
authDomain: string;
|
||||
projectId: string;
|
||||
}
|
@ -12,6 +12,7 @@ bolt.diy allows you to choose the LLM that you use for each prompt! Currently, y
|
||||
- [Entering API Keys](#entering-api-keys)
|
||||
- [1. Set API Keys in the `.env.local` File](#1-set-api-keys-in-the-envlocal-file)
|
||||
- [2. Configure API Keys Directly in the Application](#2-configure-api-keys-directly-in-the-application)
|
||||
- [Supabase / Firebase Setup Wizard](#supabase--firebase-setup-wizard)
|
||||
- [Run the Application](#run-the-application)
|
||||
- [Option 1: Without Docker](#option-1-without-docker)
|
||||
- [Option 2: With Docker](#option-2-with-docker)
|
||||
@ -118,6 +119,15 @@ Once you've configured your keys, the application will be ready to use the selec
|
||||
|
||||
---
|
||||
|
||||
### Supabase / Firebase Setup Wizard
|
||||
|
||||
bolt.diy now includes a simple setup wizard for connecting to either Supabase or Firebase.
|
||||
Open the **Setup Wizard** from the chat box to enter your API keys and automatically generate
|
||||
the necessary environment variables. Once connected, the wizard provides sample code for
|
||||
initializing the selected service using the values from your `.env.local` file.
|
||||
|
||||
---
|
||||
|
||||
## Run the Application
|
||||
|
||||
### Option 1: Without Docker
|
||||
|
13
examples/firebase.ts
Normal file
13
examples/firebase.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { initializeApp } from 'firebase/app';
|
||||
import { getAuth } from 'firebase/auth';
|
||||
import { getFirestore } from 'firebase/firestore';
|
||||
|
||||
const firebaseConfig = {
|
||||
apiKey: import.meta.env.VITE_FIREBASE_API_KEY!,
|
||||
authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN!,
|
||||
projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID!,
|
||||
};
|
||||
|
||||
const app = initializeApp(firebaseConfig);
|
||||
export const auth = getAuth(app);
|
||||
export const db = getFirestore(app);
|
6
examples/supabaseClient.ts
Normal file
6
examples/supabaseClient.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { createClient } from '@supabase/supabase-js';
|
||||
|
||||
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL!;
|
||||
const anonKey = import.meta.env.VITE_SUPABASE_ANON_KEY!;
|
||||
|
||||
export const supabase = createClient(supabaseUrl, anonKey);
|
Loading…
Reference in New Issue
Block a user