mirror of
https://github.com/stackblitz-labs/bolt.diy
synced 2025-06-25 17:56:12 +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=12288 # Consumes 26GB of VRAM
|
||||||
# DEFAULT_NUM_CTX=6144 # Consumes 24GB 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=
|
||||||
|
@ -117,3 +117,12 @@ VITE_CLOUDFLARE_ACCOUNT_ID=
|
|||||||
# DEFAULT_NUM_CTX=12288 # Consumes 26GB of VRAM
|
# DEFAULT_NUM_CTX=12288 # Consumes 26GB of VRAM
|
||||||
# DEFAULT_NUM_CTX=6144 # Consumes 24GB 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.
|
- **Download projects as ZIP** for easy portability Sync to a folder on the host.
|
||||||
- **Integration-ready Docker support** for a hassle-free setup.
|
- **Integration-ready Docker support** for a hassle-free setup.
|
||||||
- **Deploy** directly to **Netlify**, **Vercel**, or **Cloudflare Pages**
|
- **Deploy** directly to **Netlify**, **Vercel**, or **Cloudflare Pages**
|
||||||
|
- **Guided Setup Wizard** for Supabase and Firebase connections
|
||||||
- **Import Figma designs** to generate starter UI code.
|
- **Import Figma designs** to generate starter UI code.
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
@ -14,6 +14,7 @@ import { toast } from 'react-toastify';
|
|||||||
import { extractTextFromFile } from '~/utils/fileExtract';
|
import { extractTextFromFile } from '~/utils/fileExtract';
|
||||||
import { SpeechRecognitionButton } from '~/components/chat/SpeechRecognition';
|
import { SpeechRecognitionButton } from '~/components/chat/SpeechRecognition';
|
||||||
import { SupabaseConnection } from './SupabaseConnection';
|
import { SupabaseConnection } from './SupabaseConnection';
|
||||||
|
import { SetupWizard } from '~/components/setup/SetupWizard';
|
||||||
import { ExpoQrModal } from '~/components/workbench/ExpoQrModal';
|
import { ExpoQrModal } from '~/components/workbench/ExpoQrModal';
|
||||||
import styles from './BaseChat.module.scss';
|
import styles from './BaseChat.module.scss';
|
||||||
import type { ProviderInfo } from '~/types/model';
|
import type { ProviderInfo } from '~/types/model';
|
||||||
@ -353,6 +354,7 @@ export const ChatBox: React.FC<ChatBoxProps> = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
<SupabaseConnection />
|
<SupabaseConnection />
|
||||||
|
<SetupWizard />
|
||||||
<ExpoQrModal open={props.qrModalOpen} onClose={() => props.setQrModalOpen(false)} />
|
<ExpoQrModal open={props.qrModalOpen} onClose={() => props.setQrModalOpen(false)} />
|
||||||
</div>
|
</div>
|
||||||
</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)
|
- [Entering API Keys](#entering-api-keys)
|
||||||
- [1. Set API Keys in the `.env.local` File](#1-set-api-keys-in-the-envlocal-file)
|
- [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)
|
- [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)
|
- [Run the Application](#run-the-application)
|
||||||
- [Option 1: Without Docker](#option-1-without-docker)
|
- [Option 1: Without Docker](#option-1-without-docker)
|
||||||
- [Option 2: With Docker](#option-2-with-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
|
## Run the Application
|
||||||
|
|
||||||
### Option 1: Without Docker
|
### 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