This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -466,9 +466,8 @@ export function RuntimeConfigPage() {
|
||||
<table className="w-full table-fixed text-left text-sm">
|
||||
<colgroup>
|
||||
<col className="w-[22%]" />
|
||||
<col className="w-[29%]" />
|
||||
<col className="w-[19%]" />
|
||||
<col className="w-[24%]" />
|
||||
<col className="w-[30%]" />
|
||||
<col />
|
||||
<col className="w-20" />
|
||||
</colgroup>
|
||||
<thead className="sticky top-0 z-10 border-b border-border bg-card text-xs text-muted-foreground">
|
||||
@@ -476,13 +475,12 @@ export function RuntimeConfigPage() {
|
||||
<th className="px-5 py-3 font-medium">구성요소</th>
|
||||
<th className="px-5 py-3 font-medium">Key</th>
|
||||
<th className="px-5 py-3 font-medium">Value</th>
|
||||
<th className="px-5 py-3 font-medium">등록일</th>
|
||||
<th className="w-16 px-5 py-3 text-right font-medium">관리</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-border">
|
||||
{variables.length === 0 ? (
|
||||
<tr><td colSpan={5} className="px-5 py-14 text-center text-muted-foreground">등록된 설정값이 없습니다.</td></tr>
|
||||
<tr><td colSpan={4} className="px-5 py-14 text-center text-muted-foreground">등록된 설정값이 없습니다.</td></tr>
|
||||
) : null}
|
||||
{variables.map((variable) => (
|
||||
<tr key={variable.id} className="hover:bg-muted/20">
|
||||
@@ -494,7 +492,6 @@ export function RuntimeConfigPage() {
|
||||
</td>
|
||||
<td className="break-all px-4 py-3 font-mono text-xs font-medium text-foreground">{variable.key}</td>
|
||||
<td className="break-all px-4 py-3 font-mono text-xs text-muted-foreground">{variable.isSecret ? '********' : variable.value}</td>
|
||||
<td className="px-4 py-3 text-xs text-muted-foreground">{formatDate(variable.createdAt)}</td>
|
||||
<td className="px-3 py-3 text-right">
|
||||
<Button variant="ghost" className="h-8 w-8 p-0 text-rose-300 hover:text-rose-200" title="삭제" aria-label={`${variable.key} 삭제`} onClick={() => removeVariable(variable.id)} disabled={Boolean(actionKey)}>
|
||||
<Trash2 className="size-4" />
|
||||
|
||||
Reference in New Issue
Block a user