add launcher component secret setting support
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
2026-07-08 15:24:08 +09:00
parent 403d3ac0ed
commit c89210ac6a
3 changed files with 32 additions and 21 deletions
+27 -14
View File
@@ -122,7 +122,7 @@ export function LauncherComponentsPage() {
const [logs, setLogs] = useState('');
const [logLoading, setLogLoading] = useState(false);
const [envTarget, setEnvTarget] = useState<LauncherComponentDefinition | null>(null);
const [envVariables, setEnvVariables] = useState<Array<{ key: string; value: string; editable: boolean; source: string }>>([]);
const [envVariables, setEnvVariables] = useState<Array<{ key: string; value: string; isSecret: boolean; editable: boolean; source: string }>>([]);
const [envLoading, setEnvLoading] = useState(false);
const [envSaving, setEnvSaving] = useState(false);
const loadingVisible = useMinimumLoading(loading || logLoading);
@@ -187,12 +187,12 @@ export function LauncherComponentsPage() {
setEnvVariables(result.variables ?? []);
})
.catch((error: Error) => {
toast({ type: 'error', title: '환경변수 조회 실패', message: error.message });
toast({ type: 'error', title: '설정값 조회 실패', message: error.message });
})
.finally(() => setEnvLoading(false));
};
const updateEnvVariable = (index: number, patch: Partial<{ key: string; value: string }>) => {
const updateEnvVariable = (index: number, patch: Partial<{ key: string; value: string; isSecret: boolean }>) => {
setEnvVariables((prev) => prev.map((variable, variableIndex) => (
variableIndex === index ? { ...variable, ...patch } : variable
)));
@@ -211,16 +211,16 @@ export function LauncherComponentsPage() {
envTarget.workloadName,
envVariables
.filter((variable) => variable.editable)
.map((variable) => ({ key: variable.key.trim(), value: variable.value }))
.map((variable) => ({ key: variable.key.trim(), value: variable.value, isSecret: variable.isSecret }))
.filter((variable) => variable.key)
)
.then((result) => {
setEnvVariables(result.variables ?? []);
toast({ type: 'success', title: '환경변수 저장', message: `${envTarget.name} 환경변수를 반영했습니다.` });
toast({ type: 'success', title: '설정값 저장', message: `${envTarget.name} 설정값을 반영했습니다.` });
refresh();
})
.catch((error: Error) => {
toast({ type: 'error', title: '환경변수 저장 실패', message: error.message });
toast({ type: 'error', title: '설정값 저장 실패', message: error.message });
})
.finally(() => setEnvSaving(false));
};
@@ -329,7 +329,7 @@ export function LauncherComponentsPage() {
{component.type === 'backend' ? (
<Button variant="outline" onClick={() => openEnv(component)} disabled={!workload}>
<Settings className="size-4" />
환경변수
설정값
</Button>
) : null}
</div>
@@ -396,7 +396,7 @@ export function LauncherComponentsPage() {
<Modal
open={Boolean(envTarget)}
title={envTarget ? `${envTarget.name} 환경변수` : '환경변수'}
title={envTarget ? `${envTarget.name} 설정값` : '설정값'}
onClose={() => setEnvTarget(null)}
className="!max-w-4xl"
footer={
@@ -410,14 +410,15 @@ export function LauncherComponentsPage() {
>
<div className="space-y-4">
<p className="text-xs leading-5 text-muted-foreground">
저장하면 Kubernetes Deployment 환경변수가 변경되고 새 Pod로 롤아웃됩니다. Secret/ConfigMap에서 주입된 값은 여기서 직접 수정하지 않습니다.
Secret을 체크하면 Kubernetes Secret에 저장하고 secretKeyRef로 연결합니다. 체크하지 않은 값은 Deployment 환경변수로 저장합니다.
</p>
<div className="overflow-x-auto rounded-md border border-border">
<table className="w-full min-w-[720px] text-left text-sm">
<table className="w-full min-w-[820px] text-left text-sm">
<thead className="border-b border-border bg-muted/30 text-xs text-muted-foreground">
<tr>
<th className="px-4 py-3 font-medium">Key</th>
<th className="px-4 py-3 font-medium">Value</th>
<th className="w-24 px-4 py-3 font-medium">Secret</th>
<th className="w-24 px-4 py-3 font-medium">Source</th>
<th className="w-16 px-4 py-3 font-medium"></th>
</tr>
@@ -425,12 +426,12 @@ export function LauncherComponentsPage() {
<tbody className="divide-y divide-border">
{envLoading ? (
<tr>
<td colSpan={4} className="px-4 py-8 text-center text-muted-foreground">환경변수를 불러오는 중입니다.</td>
<td colSpan={5} className="px-4 py-8 text-center text-muted-foreground">설정값을 불러오는 중입니다.</td>
</tr>
) : null}
{!envLoading && envVariables.length === 0 ? (
<tr>
<td colSpan={4} className="px-4 py-8 text-center text-muted-foreground">등록된 환경변수가 없습니다.</td>
<td colSpan={5} className="px-4 py-8 text-center text-muted-foreground">등록된 설정값이 없습니다.</td>
</tr>
) : null}
{!envLoading && envVariables.map((variable, index) => (
@@ -451,6 +452,18 @@ export function LauncherComponentsPage() {
onChange={(event) => updateEnvVariable(index, { value: event.target.value })}
/>
</td>
<td className="px-4 py-3">
<label className="inline-flex items-center gap-2 text-xs text-muted-foreground">
<input
type="checkbox"
className="size-4 accent-primary"
checked={variable.isSecret}
disabled={!variable.editable}
onChange={(event) => updateEnvVariable(index, { isSecret: event.target.checked })}
/>
Secret
</label>
</td>
<td className="px-4 py-3">
<Badge tone={variable.editable ? 'blue' : 'neutral'}>{variable.source}</Badge>
</td>
@@ -473,11 +486,11 @@ export function LauncherComponentsPage() {
</div>
<Button
variant="outline"
onClick={() => setEnvVariables((prev) => [...prev, { key: '', value: '', editable: true, source: 'literal' }])}
onClick={() => setEnvVariables((prev) => [...prev, { key: '', value: '', isSecret: false, editable: true, source: 'literal' }])}
disabled={envLoading}
>
<Plus className="size-4" />
환경변수 추가
설정값 추가
</Button>
</div>
</Modal>
+2 -1
View File
@@ -425,6 +425,7 @@ export function deleteLauncherCommonSetting(id: string) {
export type RuntimeWorkloadEnvVariable = {
key: string;
value: string;
isSecret: boolean;
editable: boolean;
source: string;
};
@@ -447,7 +448,7 @@ export function saveRuntimeWorkloadEnv(
namespace: string,
kind: string,
name: string,
variables: Array<{ key: string; value: string }>
variables: Array<{ key: string; value: string; isSecret: boolean }>
) {
return apiRequest<{
namespace: string;