Enviroment variable patch
This commit is contained in:
@@ -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 } =
|
||||||
|
prev[selectedApp].variables;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
[selectedApp]: {
|
[selectedApp]: {
|
||||||
...env[selectedApp],
|
...prev[selectedApp],
|
||||||
variables: env[selectedApp].variables.filter(
|
variables: restVars,
|
||||||
(_, iv) => iv !== i
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
}));
|
};
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Trash2 className="w-3 h-3" />
|
<Trash2 className="w-3 h-3" />
|
||||||
|
|||||||
@@ -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,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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>;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user