Skip to content

Commit be96361

Browse files
committed
chore: configure environment and Docker for Supabase auth
Docker Changes: - Pass Supabase env vars as build args to frontend Dockerfile - Add VITE_SUPABASE_URL and VITE_SUPABASE_ANON_KEY to docker-compose - Ensure Vite has access to Supabase config at build time Environment: - Add SUPABASE_ANON_KEY to .env.example - Add SUPABASE_JWT_SECRET for backend token verification - Updated frontend to use Supabase environment variables Fixes: - Resolve Supabase client initialization errors - Fix "supabaseUrl is required" runtime error - Move Supabase client creation outside component (singleton pattern) - Prevent multiple GoTrueClient instances warning - Fix Dashboard CSS import path - Fix App export/import mismatch Technical: Vite env vars must be set at build time, not runtime
1 parent 73c3ec0 commit be96361

5 files changed

Lines changed: 303 additions & 2 deletions

File tree

.env.example

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ PINECONE_INDEX_NAME=codeintel
1313
# Supabase (Required)
1414
# Get from: https://app.supabase.com/project/_/settings/api
1515
SUPABASE_URL=https://your-project.supabase.co
16-
SUPABASE_KEY=eyJ...
16+
SUPABASE_ANON_KEY=eyJ...
17+
SUPABASE_JWT_SECRET=your-jwt-secret # From Project Settings → API → JWT Secret
1718

1819
# Backend API
1920
API_KEY=change-this-secret-key-for-production

docker-compose.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,17 @@ services:
5555
build:
5656
context: ./frontend
5757
dockerfile: Dockerfile
58+
args:
59+
- VITE_API_URL=http://localhost:8000
60+
- VITE_SUPABASE_URL=${SUPABASE_URL}
61+
- VITE_SUPABASE_ANON_KEY=${SUPABASE_KEY}
5862
container_name: codeintel-frontend
5963
ports:
6064
- "3000:80"
6165
environment:
6266
- VITE_API_URL=http://localhost:8000
67+
- VITE_SUPABASE_URL=${SUPABASE_URL}
68+
- VITE_SUPABASE_ANON_KEY=${SUPABASE_KEY}
6369
depends_on:
6470
- backend
6571
healthcheck:

frontend/Dockerfile

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,16 @@ FROM node:20-alpine AS builder
33

44
WORKDIR /app
55

6+
# Accept build arguments for Vite env vars
7+
ARG VITE_API_URL
8+
ARG VITE_SUPABASE_URL
9+
ARG VITE_SUPABASE_ANON_KEY
10+
11+
# Set as environment variables for Vite build
12+
ENV VITE_API_URL=$VITE_API_URL
13+
ENV VITE_SUPABASE_URL=$VITE_SUPABASE_URL
14+
ENV VITE_SUPABASE_ANON_KEY=$VITE_SUPABASE_ANON_KEY
15+
616
# Copy package files
717
COPY package*.json ./
818

frontend/components.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,7 @@
1818
"lib": "@/lib",
1919
"hooks": "@/hooks"
2020
},
21-
"registries": {}
21+
"registries": {
22+
"@shadcn": "https://ui.shadcn.com/r/styles/new-york.json"
23+
}
2224
}
Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
import { useState, useEffect } from 'react'
2+
import { Toaster } from '@/components/ui/sonner'
3+
import { toast } from 'sonner'
4+
import { RepoList } from './RepoList'
5+
import { AddRepoForm } from './AddRepoForm'
6+
import { SearchPanel } from './SearchPanel'
7+
import { DependencyGraph } from './DependencyGraph'
8+
import { RepoOverview } from './RepoOverview'
9+
import { StyleInsights } from './StyleInsights'
10+
import { ImpactAnalyzer } from './ImpactAnalyzer'
11+
import { PerformanceDashboard } from './PerformanceDashboard'
12+
import { UserNav } from './UserNav'
13+
import type { Repository } from '../types'
14+
15+
const API_URL = 'http://localhost:8000'
16+
const API_KEY = 'dev-secret-key'
17+
18+
type RepoTab = 'overview' | 'search' | 'dependencies' | 'insights' | 'impact'
19+
20+
export function Dashboard() {
21+
const [repos, setRepos] = useState<Repository[]>([])
22+
const [selectedRepo, setSelectedRepo] = useState<string | null>(null)
23+
const [activeTab, setActiveTab] = useState<RepoTab>('overview')
24+
const [loading, setLoading] = useState(false)
25+
const [showPerformance, setShowPerformance] = useState(false)
26+
27+
const fetchRepos = async () => {
28+
try {
29+
const response = await fetch(`${API_URL}/api/repos`, {
30+
headers: { 'Authorization': `Bearer ${API_KEY}` }
31+
})
32+
const data = await response.json()
33+
setRepos(data.repositories || [])
34+
} catch (error) {
35+
console.error('Error fetching repos:', error)
36+
}
37+
}
38+
39+
useEffect(() => {
40+
fetchRepos()
41+
const interval = setInterval(fetchRepos, 30000)
42+
return () => clearInterval(interval)
43+
}, [])
44+
45+
const handleAddRepo = async (gitUrl: string, branch: string) => {
46+
try {
47+
setLoading(true)
48+
const name = gitUrl.split('/').pop()?.replace('.git', '') || 'unknown'
49+
50+
const response = await fetch(`${API_URL}/api/repos`, {
51+
method: 'POST',
52+
headers: {
53+
'Authorization': `Bearer ${API_KEY}`,
54+
'Content-Type': 'application/json'
55+
},
56+
body: JSON.stringify({ name, git_url: gitUrl, branch })
57+
})
58+
59+
const data = await response.json()
60+
61+
await fetch(`${API_URL}/api/repos/${data.repo_id}/index`, {
62+
method: 'POST',
63+
headers: { 'Authorization': `Bearer ${API_KEY}` }
64+
})
65+
66+
await fetchRepos()
67+
toast.success('Repository added!', {
68+
description: `${name} is now being indexed`
69+
})
70+
} catch (error) {
71+
console.error('Error adding repo:', error)
72+
toast.error('Failed to add repository', {
73+
description: 'Please check the Git URL and try again'
74+
})
75+
} finally {
76+
setLoading(false)
77+
}
78+
}
79+
80+
const handleReindex = async () => {
81+
if (!selectedRepo) return
82+
83+
try {
84+
setLoading(true)
85+
await fetch(`${API_URL}/api/repos/${selectedRepo}/index`, {
86+
method: 'POST',
87+
headers: { 'Authorization': `Bearer ${API_KEY}` }
88+
})
89+
await fetchRepos()
90+
// RepoOverview component will show the toast and progress
91+
} catch (error) {
92+
toast.error('Re-indexing failed', {
93+
description: 'Please check the console for details'
94+
})
95+
} finally {
96+
setLoading(false)
97+
}
98+
}
99+
100+
const selectedRepoData = repos.find(r => r.id === selectedRepo)
101+
const isRepoView = selectedRepo && selectedRepoData
102+
103+
return (
104+
<div className="min-h-screen bg-gray-50">
105+
<nav className="bg-white border-b border-gray-200">
106+
<div className="max-w-7xl mx-auto px-6">
107+
<div className="flex items-center justify-between h-16">
108+
<div className="flex items-center gap-2">
109+
<div className="w-8 h-8 rounded-lg bg-blue-600 flex items-center justify-center">
110+
<span className="text-white font-bold text-sm">CI</span>
111+
</div>
112+
<span className="font-semibold text-gray-900">CodeIntel</span>
113+
<span className="text-xs text-gray-400 ml-2">MCP Server</span>
114+
</div>
115+
116+
<div className="flex items-center gap-4">
117+
<button
118+
onClick={() => setShowPerformance(!showPerformance)}
119+
className={`text-xs px-3 py-1.5 rounded-md transition-colors ${
120+
showPerformance
121+
? 'bg-blue-100 text-blue-700 font-medium'
122+
: 'text-gray-600 hover:bg-gray-100'
123+
}`}
124+
>
125+
📊 Performance
126+
</button>
127+
<div className="flex items-center gap-2 text-xs text-gray-500">
128+
<div className="w-2 h-2 rounded-full bg-green-500" />
129+
<span>API Connected</span>
130+
</div>
131+
<div className="text-xs text-gray-400 border-l border-gray-200 pl-4">
132+
{repos.length} repos • {repos.filter(r => r.status === 'indexed').length} indexed
133+
</div>
134+
<div className="border-l border-gray-200 pl-4">
135+
<UserNav />
136+
</div>
137+
</div>
138+
</div>
139+
</div>
140+
</nav>
141+
142+
<main className="max-w-7xl mx-auto px-6 py-8">
143+
{/* Performance Dashboard Overlay */}
144+
{showPerformance && (
145+
<div className="mb-6">
146+
<PerformanceDashboard apiUrl={API_URL} apiKey={API_KEY} />
147+
</div>
148+
)}
149+
150+
{!isRepoView && (
151+
<div className="space-y-6">
152+
<div className="flex items-center justify-between">
153+
<div>
154+
<h1 className="text-2xl font-semibold text-gray-900">Repositories</h1>
155+
<p className="text-sm text-gray-600 mt-1">Manage and index your codebases</p>
156+
</div>
157+
<AddRepoForm onAdd={handleAddRepo} loading={loading} />
158+
</div>
159+
160+
<RepoList
161+
repos={repos}
162+
selectedRepo={selectedRepo}
163+
onSelect={(id) => {
164+
setSelectedRepo(id)
165+
setActiveTab('overview')
166+
}}
167+
/>
168+
</div>
169+
)}
170+
171+
{isRepoView && (
172+
<div>
173+
<div className="mb-6">
174+
<button
175+
onClick={() => {
176+
setSelectedRepo(null)
177+
setActiveTab('overview')
178+
}}
179+
className="flex items-center gap-2 text-sm text-gray-600 hover:text-gray-900 mb-4"
180+
>
181+
<span></span>
182+
<span>Back to Repositories</span>
183+
</button>
184+
185+
<div className="flex items-center justify-between">
186+
<div>
187+
<h1 className="text-2xl font-semibold text-gray-900">{selectedRepoData.name}</h1>
188+
<p className="text-sm text-gray-600 mt-1 font-mono">{selectedRepoData.git_url}</p>
189+
</div>
190+
191+
{selectedRepoData.status === 'indexed' && (
192+
<span className="badge-success">✓ Indexed</span>
193+
)}
194+
</div>
195+
</div>
196+
197+
<div className="border-b border-gray-200 mb-6">
198+
<div className="flex gap-6 overflow-x-auto">
199+
<button
200+
onClick={() => setActiveTab('overview')}
201+
className={`pb-3 border-b-2 transition-colors whitespace-nowrap ${
202+
activeTab === 'overview'
203+
? 'border-blue-600 text-gray-900 font-medium'
204+
: 'border-transparent text-gray-600 hover:text-gray-900'
205+
}`}
206+
>
207+
Overview
208+
</button>
209+
<button
210+
onClick={() => setActiveTab('search')}
211+
className={`pb-3 border-b-2 transition-colors whitespace-nowrap ${
212+
activeTab === 'search'
213+
? 'border-blue-600 text-gray-900 font-medium'
214+
: 'border-transparent text-gray-600 hover:text-gray-900'
215+
}`}
216+
>
217+
Search
218+
</button>
219+
<button
220+
onClick={() => setActiveTab('dependencies')}
221+
className={`pb-3 border-b-2 transition-colors whitespace-nowrap ${
222+
activeTab === 'dependencies'
223+
? 'border-blue-600 text-gray-900 font-medium'
224+
: 'border-transparent text-gray-600 hover:text-gray-900'
225+
}`}
226+
>
227+
Dependencies
228+
</button>
229+
<button
230+
onClick={() => setActiveTab('insights')}
231+
className={`pb-3 border-b-2 transition-colors whitespace-nowrap ${
232+
activeTab === 'insights'
233+
? 'border-blue-600 text-gray-900 font-medium'
234+
: 'border-transparent text-gray-600 hover:text-gray-900'
235+
}`}
236+
>
237+
Code Style
238+
</button>
239+
<button
240+
onClick={() => setActiveTab('impact')}
241+
className={`pb-3 border-b-2 transition-colors whitespace-nowrap ${
242+
activeTab === 'impact'
243+
? 'border-blue-600 text-gray-900 font-medium'
244+
: 'border-transparent text-gray-600 hover:text-gray-900'
245+
}`}
246+
>
247+
Impact
248+
</button>
249+
</div>
250+
</div>
251+
252+
{activeTab === 'overview' && (
253+
<RepoOverview
254+
repo={selectedRepoData}
255+
onReindex={handleReindex}
256+
apiUrl={API_URL}
257+
apiKey={API_KEY}
258+
/>
259+
)}
260+
261+
{activeTab === 'search' && (
262+
<SearchPanel repoId={selectedRepo} apiUrl={API_URL} apiKey={API_KEY} />
263+
)}
264+
265+
{activeTab === 'dependencies' && (
266+
<DependencyGraph repoId={selectedRepo} apiUrl={API_URL} apiKey={API_KEY} />
267+
)}
268+
269+
{activeTab === 'insights' && (
270+
<StyleInsights repoId={selectedRepo} apiUrl={API_URL} apiKey={API_KEY} />
271+
)}
272+
273+
{activeTab === 'impact' && (
274+
<ImpactAnalyzer repoId={selectedRepo} apiUrl={API_URL} apiKey={API_KEY} />
275+
)}
276+
</div>
277+
)}
278+
</main>
279+
<Toaster />
280+
</div>
281+
)
282+
}

0 commit comments

Comments
 (0)