Enviroment variable patch

This commit is contained in:
2026-01-02 19:40:00 +01:00
parent f83e735807
commit fd3b8bda02
4 changed files with 70 additions and 46 deletions
+54 -38
View File
@@ -33,16 +33,6 @@ export default function EnvironmentPage() {
fetch(); fetch();
}, []); }, []);
const currentGroup = envGroups[selectedApp];
const toggleSecret = (key: string) => {
setShowSecrets((prev) => ({ ...prev, [key]: !prev[key] }));
};
const maskValue = (value: string) => {
return "•".repeat(Math.min(value.length, 24));
};
const handleSave = async () => { const handleSave = async () => {
setIsSaving(true); setIsSaving(true);
try { try {
@@ -54,6 +44,12 @@ export default function EnvironmentPage() {
} }
}; };
const currentGroup = envGroups[selectedApp] || {
id: "",
name: "",
variables: {},
};
return ( return (
<DashboardLayout <DashboardLayout
title="Environment" title="Environment"
@@ -90,8 +86,12 @@ export default function EnvironmentPage() {
</DropdownMenu> </DropdownMenu>
</CardTitle> </CardTitle>
<p className="text-xs text-muted-foreground mt-1"> <p className="text-xs text-muted-foreground mt-1">
{currentGroup?.variables.length} variables •{" "} {Object.keys(currentGroup?.variables).length} variables •{" "}
{currentGroup?.variables.filter((v) => v.secret).length} secrets {
Object.values(currentGroup?.variables).filter((v) => v.secret)
.length
}{" "}
secrets
</p> </p>
</div> </div>
@@ -129,9 +129,9 @@ export default function EnvironmentPage() {
{/* Rows */} {/* Rows */}
<div className="divide-y divide-border md:divide-none"> <div className="divide-y divide-border md:divide-none">
{currentGroup?.variables.map((variable, i) => ( {Object.entries(currentGroup?.variables).map(([key, variable]) => (
<div <div
key={variable.key} key={key}
className=" className="
py-4 md:py-3 py-4 md:py-3
flex flex-col gap-3 flex flex-col gap-3
@@ -141,23 +141,33 @@ export default function EnvironmentPage() {
{/* Key */} {/* Key */}
<div className="md:col-span-4 flex items-center gap-2"> <div className="md:col-span-4 flex items-center gap-2">
{variable.secret && <Lock className="w-3 h-3 text-warning" />} {variable.secret && <Lock className="w-3 h-3 text-warning" />}
<span className="font-mono text-sm">{variable.key}</span> <span className="font-mono text-sm">{key}</span>
</div> </div>
{/* Value */} {/* Value */}
<div className="md:col-span-6"> <div className="md:col-span-6">
<Input <Input
type={ type={
variable.secret && !showSecrets[variable.key] variable.secret && !showSecrets[key] ? "password" : "text"
? "password"
: "text"
} }
value={ value={variable.value}
variable.secret && !showSecrets[variable.key] onChange={(e) => {
? maskValue(variable.value) const newValue = e.target.value;
: variable.value
} setEnvGroups((prev) => ({
readOnly ...prev,
[selectedApp]: {
...prev[selectedApp],
variables: {
...prev[selectedApp].variables,
[key]: {
...prev[selectedApp].variables[key],
value: newValue,
},
},
},
}));
}}
className="font-mono text-xs h-8 bg-background border-border" className="font-mono text-xs h-8 bg-background border-border"
/> />
</div> </div>
@@ -168,9 +178,14 @@ export default function EnvironmentPage() {
<Button <Button
variant="ghost" variant="ghost"
size="icon-sm" size="icon-sm"
onClick={() => toggleSecret(variable.key)} onClick={() =>
setShowSecrets((prev) => ({
...prev,
[key]: !prev[key],
}))
}
> >
{showSecrets[variable.key] ? ( {showSecrets[key] ? (
<EyeOff className="w-3 h-3" /> <EyeOff className="w-3 h-3" />
) : ( ) : (
<Eye className="w-3 h-3" /> <Eye className="w-3 h-3" />
@@ -182,9 +197,7 @@ export default function EnvironmentPage() {
size="icon-sm" size="icon-sm"
className="hover:text-primary" className="hover:text-primary"
onClick={() => onClick={() =>
navigator.clipboard.writeText( navigator.clipboard.writeText(`${key}=${variable.value}`)
`${variable.key}=${variable.value}`
)
} }
> >
<Copy className="w-3 h-3" /> <Copy className="w-3 h-3" />
@@ -194,15 +207,18 @@ export default function EnvironmentPage() {
size="icon-sm" size="icon-sm"
className="hover:text-destructive" className="hover:text-destructive"
onClick={() => { onClick={() => {
setEnvGroups((env) => ({ setEnvGroups((prev) => {
...env, const { [key]: _, ...restVars } =
[selectedApp]: { prev[selectedApp].variables;
...env[selectedApp],
variables: env[selectedApp].variables.filter( return {
(_, iv) => iv !== i ...prev,
), [selectedApp]: {
}, ...prev[selectedApp],
})); variables: restVars,
},
};
});
}} }}
> >
<Trash2 className="w-3 h-3" /> <Trash2 className="w-3 h-3" />
+11 -5
View File
@@ -34,12 +34,18 @@ export function AddVariableDialog({
const handleAdd = () => { const handleAdd = () => {
if (!key) return; if (!key) return;
const newVariable: EnvVariable = { key, value, secret };
setEnvGroups((env) => ({ setEnvGroups((prev) => ({
...env, ...prev,
[selectedApp]: { [selectedApp]: {
...currentGroup, ...prev[selectedApp],
variables: [...currentGroup.variables, newVariable], variables: {
...prev[selectedApp].variables,
[key]: {
value,
secret,
},
},
}, },
})); }));
+4 -1
View File
@@ -59,7 +59,10 @@ export async function getEnv(): Promise<Record<string, Env>> {
return merged; return merged;
} }
export async function setEnv(appId: string, variables: EnvVariable[]) { export async function setEnv(
appId: string,
variables: Record<string, EnvVariable>
) {
const nodes = await getNodes(); const nodes = await getNodes();
nodes.forEach(async (n) => { nodes.forEach(async (n) => {
+1 -2
View File
@@ -79,7 +79,6 @@ export interface Log {
} }
export interface EnvVariable { export interface EnvVariable {
key: string;
value: string; value: string;
secret: boolean; secret: boolean;
} }
@@ -87,5 +86,5 @@ export interface EnvVariable {
export interface Env { export interface Env {
id: string; id: string;
name: string; name: string;
variables: EnvVariable[]; variables: Record<string, EnvVariable>;
} }