Skip to content

Commit bfb2893

Browse files
committed
fix env vars
1 parent ace0add commit bfb2893

3 files changed

Lines changed: 179 additions & 50 deletions

File tree

src/pages/Dashboard/DashboardMainSection.tsx

Lines changed: 164 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ import {
2727
RotateCcw,
2828
Plus,
2929
Settings,
30+
Eye,
31+
EyeOff,
3032
} from 'lucide-react';
3133
import type { Team } from '@/services/types';
3234
import {
@@ -101,6 +103,7 @@ export default function DashboardMainSection({
101103
// Environment variables state
102104
const [envVars, setEnvVars] = useState<Array<{ key: string; value: string }>>([]);
103105
const [isEnvVarsLoaded, setIsEnvVarsLoaded] = useState(false);
106+
const [showDevelopmentVars, setShowDevelopmentVars] = useState(false);
104107

105108
// Load environment variables from localStorage on mount and when team changes
106109
useEffect(() => {
@@ -462,7 +465,7 @@ export default function DashboardMainSection({
462465
await streamingDeploy.deploy({
463466
githubUrl: githubUrl.trim(),
464467
dataFile: dataFile || undefined,
465-
envVars: Object.keys(envVarsObject).length > 0 ? envVarsObject : undefined,
468+
extraEnvVars: Object.keys(envVarsObject).length > 0 ? envVarsObject : undefined,
466469
});
467470

468471
setGithubUrl(''); // Clear input on success
@@ -1373,56 +1376,171 @@ export default function DashboardMainSection({
13731376
</div>
13741377

13751378
{/* Environment Variables Section */}
1376-
<Card className="p-4">
1377-
<div className="space-y-3">
1378-
<div className="flex items-center justify-between">
1379+
<div className="space-y-3">
1380+
{/* Issued Environment Variables (from backend) */}
1381+
<Card className="p-4">
1382+
<div className="space-y-3">
13791383
<div className="flex items-center gap-2">
13801384
<Settings className="h-4 w-4 text-gray-600" />
1381-
<span className="text-sm font-medium text-gray-700">Environment Variables</span>
1385+
<span className="text-sm font-medium text-gray-700">
1386+
Issued Environment Variables
1387+
</span>
13821388
</div>
1383-
<Button
1384-
variant="outline"
1385-
size="sm"
1386-
onClick={handleAddEnvVar}
1387-
className="flex items-center gap-1"
1388-
>
1389-
<Plus className="h-3 w-3" />
1390-
Add
1391-
</Button>
1392-
</div>
1393-
1394-
{envVars.length > 0 && (
1395-
<div className="space-y-2">
1396-
{envVars.map((envVar, index) => (
1397-
<div key={index} className="flex gap-2 items-center">
1398-
<input
1399-
type="text"
1400-
value={envVar.key}
1401-
onChange={(e) => handleEnvVarKeyChange(index, e.target.value)}
1402-
placeholder="KEY"
1403-
className="flex-1 p-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent font-mono"
1404-
/>
1405-
<input
1406-
type="text"
1407-
value={envVar.value}
1408-
onChange={(e) => handleEnvVarValueChange(index, e.target.value)}
1409-
placeholder="value"
1410-
className="flex-1 p-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent font-mono"
1411-
/>
1412-
<button
1413-
type="button"
1414-
onClick={() => handleRemoveEnvVar(index)}
1415-
className="text-gray-400 hover:text-red-600 p-1"
1416-
aria-label="Remove variable"
1417-
>
1418-
<X className="h-4 w-4" />
1419-
</button>
1389+
{team.environments && team.environments.length > 0 ? (
1390+
<div className="space-y-4">
1391+
{/* Production subsection */}
1392+
<div>
1393+
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-2">
1394+
Production
1395+
</div>
1396+
<div className="space-y-2">
1397+
{team.environments
1398+
.filter((e) => e.scope === 'PRODUCTION')
1399+
.map((env) => (
1400+
<div
1401+
key={env.id}
1402+
className="flex gap-2 items-center font-mono text-sm"
1403+
>
1404+
<span className="flex-1 text-gray-700">{env.keyName}</span>
1405+
<span className="flex-1 text-gray-600 truncate">
1406+
{env.isSecret && !canBypassLock
1407+
? '••••••••'
1408+
: env.keyValue ?? '••••••••'}
1409+
</span>
1410+
{env.isSecret && (
1411+
<Lock className="h-3.5 w-3.5 text-gray-400 shrink-0" />
1412+
)}
1413+
</div>
1414+
))}
1415+
</div>
14201416
</div>
1421-
))}
1417+
{/* Development subsection - hidden by default */}
1418+
{team.environments.some((e) => e.scope === 'DEVELOPMENT') && (
1419+
<div>
1420+
<div className="flex items-center gap-2 mb-2">
1421+
<div className="text-xs font-medium text-gray-500 uppercase tracking-wide">
1422+
Development
1423+
</div>
1424+
<Button
1425+
variant="ghost"
1426+
size="sm"
1427+
onClick={() =>
1428+
setShowDevelopmentVars((v) => !v)
1429+
}
1430+
className="h-6 px-2 text-xs text-gray-600"
1431+
>
1432+
{showDevelopmentVars ? (
1433+
<>
1434+
<EyeOff className="h-3 w-3 mr-1" />
1435+
Hide
1436+
</>
1437+
) : (
1438+
<>
1439+
<Eye className="h-3 w-3 mr-1" />
1440+
Show
1441+
</>
1442+
)}
1443+
</Button>
1444+
</div>
1445+
{showDevelopmentVars ? (
1446+
<>
1447+
<p className="text-xs text-amber-600 mb-2">
1448+
Development variables are not injected at runtime. They are for
1449+
local development reference only.
1450+
</p>
1451+
<div className="space-y-2">
1452+
{team.environments
1453+
.filter((e) => e.scope === 'DEVELOPMENT')
1454+
.map((env) => (
1455+
<div
1456+
key={env.id}
1457+
className="flex gap-2 items-center font-mono text-sm"
1458+
>
1459+
<span className="flex-1 text-gray-700">{env.keyName}</span>
1460+
<span className="flex-1 text-gray-600 truncate">
1461+
{env.isSecret && !canBypassLock
1462+
? '••••••••'
1463+
: env.keyValue ?? '••••••••'}
1464+
</span>
1465+
{env.isSecret && (
1466+
<Lock className="h-3.5 w-3.5 text-gray-400 shrink-0" />
1467+
)}
1468+
</div>
1469+
))}
1470+
</div>
1471+
</>
1472+
) : (
1473+
<p className="text-xs text-gray-400 italic">
1474+
{team.environments.filter((e) => e.scope === 'DEVELOPMENT').length}{' '}
1475+
development variable(s) hidden
1476+
</p>
1477+
)}
1478+
</div>
1479+
)}
1480+
</div>
1481+
) : (
1482+
<p className="text-sm text-gray-500">No issued environment variables.</p>
1483+
)}
1484+
</div>
1485+
</Card>
1486+
1487+
{/* Extra Environment Variables (user-added for deployment) */}
1488+
<Card className="p-4">
1489+
<div className="space-y-3">
1490+
<div className="flex items-center justify-between">
1491+
<div className="flex items-center gap-2">
1492+
<Plus className="h-4 w-4 text-gray-600" />
1493+
<span className="text-sm font-medium text-gray-700">
1494+
Extra Environment Variables
1495+
</span>
1496+
</div>
1497+
<Button
1498+
variant="outline"
1499+
size="sm"
1500+
onClick={handleAddEnvVar}
1501+
className="flex items-center gap-1"
1502+
>
1503+
<Plus className="h-3 w-3" />
1504+
Add
1505+
</Button>
14221506
</div>
1423-
)}
1424-
</div>
1425-
</Card>
1507+
<p className="text-xs text-gray-500">
1508+
Add custom variables to inject during deployment (in addition to issued
1509+
variables).
1510+
</p>
1511+
{envVars.length > 0 && (
1512+
<div className="space-y-2">
1513+
{envVars.map((envVar, index) => (
1514+
<div key={index} className="flex gap-2 items-center">
1515+
<input
1516+
type="text"
1517+
value={envVar.key}
1518+
onChange={(e) => handleEnvVarKeyChange(index, e.target.value)}
1519+
placeholder="KEY"
1520+
className="flex-1 p-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent font-mono"
1521+
/>
1522+
<input
1523+
type="text"
1524+
value={envVar.value}
1525+
onChange={(e) => handleEnvVarValueChange(index, e.target.value)}
1526+
placeholder="value"
1527+
className="flex-1 p-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent font-mono"
1528+
/>
1529+
<button
1530+
type="button"
1531+
onClick={() => handleRemoveEnvVar(index)}
1532+
className="text-gray-400 hover:text-red-600 p-1"
1533+
aria-label="Remove variable"
1534+
>
1535+
<X className="h-4 w-4" />
1536+
</button>
1537+
</div>
1538+
))}
1539+
</div>
1540+
)}
1541+
</div>
1542+
</Card>
1543+
</div>
14261544

14271545
{deploymentSuccess && (
14281546
<div className="flex items-center gap-2 text-green-600 text-sm">

src/services/projects.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export interface DeployProjectData {
55
teamId: number
66
githubUrl: string
77
dataFile?: File // Optional data file for deployment
8-
envVars?: Record<string, string> // Optional environment variables
8+
extraEnvVars?: Record<string, string> // Optional environment variables
99
}
1010

1111
export interface ContainerLog {
@@ -68,9 +68,9 @@ export const projectServices = {
6868
formData.append('dataFile', data.dataFile)
6969
}
7070

71-
// Append envVars as JSON string if provided
72-
if (data.envVars) {
73-
formData.append('envVars', JSON.stringify(data.envVars))
71+
// Append extraEnvVars as JSON string if provided
72+
if (data.extraEnvVars) {
73+
formData.append('extraEnvVars', JSON.stringify(data.extraEnvVars))
7474
}
7575

7676
// Get headers from axios instance for consistency

src/services/types.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,16 @@ export type Enrollment = {
6767
user?: User
6868
}
6969

70+
// Team environment variable (from backend)
71+
export type TeamEnvironment = {
72+
id: number
73+
teamId: number
74+
keyName: string
75+
keyValue?: string
76+
scope: 'PRODUCTION' | 'DEVELOPMENT'
77+
isSecret: boolean
78+
}
79+
7080
// Team entity
7181
export type Team = {
7282
id: number
@@ -75,6 +85,7 @@ export type Team = {
7585
courseOfferingId: number
7686
createdAt: string
7787
hallOfFame?: boolean
88+
environments?: TeamEnvironment[]
7889
courseOffering?: CourseOffering
7990
members?: TeamMember[]
8091
projects?: Project[]

0 commit comments

Comments
 (0)