This commit is contained in:
@@ -122,7 +122,7 @@ export function LauncherComponentsPage() {
|
|||||||
const [logs, setLogs] = useState('');
|
const [logs, setLogs] = useState('');
|
||||||
const [logLoading, setLogLoading] = useState(false);
|
const [logLoading, setLogLoading] = useState(false);
|
||||||
const [envTarget, setEnvTarget] = useState<LauncherComponentDefinition | null>(null);
|
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 [envLoading, setEnvLoading] = useState(false);
|
||||||
const [envSaving, setEnvSaving] = useState(false);
|
const [envSaving, setEnvSaving] = useState(false);
|
||||||
const loadingVisible = useMinimumLoading(loading || logLoading);
|
const loadingVisible = useMinimumLoading(loading || logLoading);
|
||||||
@@ -187,12 +187,12 @@ export function LauncherComponentsPage() {
|
|||||||
setEnvVariables(result.variables ?? []);
|
setEnvVariables(result.variables ?? []);
|
||||||
})
|
})
|
||||||
.catch((error: Error) => {
|
.catch((error: Error) => {
|
||||||
toast({ type: 'error', title: '환경변수 조회 실패', message: error.message });
|
toast({ type: 'error', title: '설정값 조회 실패', message: error.message });
|
||||||
})
|
})
|
||||||
.finally(() => setEnvLoading(false));
|
.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) => (
|
setEnvVariables((prev) => prev.map((variable, variableIndex) => (
|
||||||
variableIndex === index ? { ...variable, ...patch } : variable
|
variableIndex === index ? { ...variable, ...patch } : variable
|
||||||
)));
|
)));
|
||||||
@@ -211,16 +211,16 @@ export function LauncherComponentsPage() {
|
|||||||
envTarget.workloadName,
|
envTarget.workloadName,
|
||||||
envVariables
|
envVariables
|
||||||
.filter((variable) => variable.editable)
|
.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)
|
.filter((variable) => variable.key)
|
||||||
)
|
)
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
setEnvVariables(result.variables ?? []);
|
setEnvVariables(result.variables ?? []);
|
||||||
toast({ type: 'success', title: '환경변수 저장', message: `${envTarget.name} 환경변수를 반영했습니다.` });
|
toast({ type: 'success', title: '설정값 저장', message: `${envTarget.name} 설정값을 반영했습니다.` });
|
||||||
refresh();
|
refresh();
|
||||||
})
|
})
|
||||||
.catch((error: Error) => {
|
.catch((error: Error) => {
|
||||||
toast({ type: 'error', title: '환경변수 저장 실패', message: error.message });
|
toast({ type: 'error', title: '설정값 저장 실패', message: error.message });
|
||||||
})
|
})
|
||||||
.finally(() => setEnvSaving(false));
|
.finally(() => setEnvSaving(false));
|
||||||
};
|
};
|
||||||
@@ -329,7 +329,7 @@ export function LauncherComponentsPage() {
|
|||||||
{component.type === 'backend' ? (
|
{component.type === 'backend' ? (
|
||||||
<Button variant="outline" onClick={() => openEnv(component)} disabled={!workload}>
|
<Button variant="outline" onClick={() => openEnv(component)} disabled={!workload}>
|
||||||
<Settings className="size-4" />
|
<Settings className="size-4" />
|
||||||
환경변수
|
설정값
|
||||||
</Button>
|
</Button>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
@@ -396,7 +396,7 @@ export function LauncherComponentsPage() {
|
|||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
open={Boolean(envTarget)}
|
open={Boolean(envTarget)}
|
||||||
title={envTarget ? `${envTarget.name} 환경변수` : '환경변수'}
|
title={envTarget ? `${envTarget.name} 설정값` : '설정값'}
|
||||||
onClose={() => setEnvTarget(null)}
|
onClose={() => setEnvTarget(null)}
|
||||||
className="!max-w-4xl"
|
className="!max-w-4xl"
|
||||||
footer={
|
footer={
|
||||||
@@ -410,14 +410,15 @@ export function LauncherComponentsPage() {
|
|||||||
>
|
>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<p className="text-xs leading-5 text-muted-foreground">
|
<p className="text-xs leading-5 text-muted-foreground">
|
||||||
저장하면 Kubernetes Deployment 환경변수가 변경되고 새 Pod로 롤아웃됩니다. Secret/ConfigMap에서 주입된 값은 여기서 직접 수정하지 않습니다.
|
Secret을 체크하면 Kubernetes Secret에 저장하고 secretKeyRef로 연결합니다. 체크하지 않은 값은 Deployment 환경변수로 저장합니다.
|
||||||
</p>
|
</p>
|
||||||
<div className="overflow-x-auto rounded-md border border-border">
|
<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">
|
<thead className="border-b border-border bg-muted/30 text-xs text-muted-foreground">
|
||||||
<tr>
|
<tr>
|
||||||
<th className="px-4 py-3 font-medium">Key</th>
|
<th className="px-4 py-3 font-medium">Key</th>
|
||||||
<th className="px-4 py-3 font-medium">Value</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-24 px-4 py-3 font-medium">Source</th>
|
||||||
<th className="w-16 px-4 py-3 font-medium"></th>
|
<th className="w-16 px-4 py-3 font-medium"></th>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -425,12 +426,12 @@ export function LauncherComponentsPage() {
|
|||||||
<tbody className="divide-y divide-border">
|
<tbody className="divide-y divide-border">
|
||||||
{envLoading ? (
|
{envLoading ? (
|
||||||
<tr>
|
<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>
|
</tr>
|
||||||
) : null}
|
) : null}
|
||||||
{!envLoading && envVariables.length === 0 ? (
|
{!envLoading && envVariables.length === 0 ? (
|
||||||
<tr>
|
<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>
|
</tr>
|
||||||
) : null}
|
) : null}
|
||||||
{!envLoading && envVariables.map((variable, index) => (
|
{!envLoading && envVariables.map((variable, index) => (
|
||||||
@@ -451,6 +452,18 @@ export function LauncherComponentsPage() {
|
|||||||
onChange={(event) => updateEnvVariable(index, { value: event.target.value })}
|
onChange={(event) => updateEnvVariable(index, { value: event.target.value })}
|
||||||
/>
|
/>
|
||||||
</td>
|
</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">
|
<td className="px-4 py-3">
|
||||||
<Badge tone={variable.editable ? 'blue' : 'neutral'}>{variable.source}</Badge>
|
<Badge tone={variable.editable ? 'blue' : 'neutral'}>{variable.source}</Badge>
|
||||||
</td>
|
</td>
|
||||||
@@ -473,11 +486,11 @@ export function LauncherComponentsPage() {
|
|||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
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}
|
disabled={envLoading}
|
||||||
>
|
>
|
||||||
<Plus className="size-4" />
|
<Plus className="size-4" />
|
||||||
환경변수 추가
|
설정값 추가
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|||||||
@@ -425,6 +425,7 @@ export function deleteLauncherCommonSetting(id: string) {
|
|||||||
export type RuntimeWorkloadEnvVariable = {
|
export type RuntimeWorkloadEnvVariable = {
|
||||||
key: string;
|
key: string;
|
||||||
value: string;
|
value: string;
|
||||||
|
isSecret: boolean;
|
||||||
editable: boolean;
|
editable: boolean;
|
||||||
source: string;
|
source: string;
|
||||||
};
|
};
|
||||||
@@ -447,7 +448,7 @@ export function saveRuntimeWorkloadEnv(
|
|||||||
namespace: string,
|
namespace: string,
|
||||||
kind: string,
|
kind: string,
|
||||||
name: string,
|
name: string,
|
||||||
variables: Array<{ key: string; value: string }>
|
variables: Array<{ key: string; value: string; isSecret: boolean }>
|
||||||
) {
|
) {
|
||||||
return apiRequest<{
|
return apiRequest<{
|
||||||
namespace: string;
|
namespace: string;
|
||||||
|
|||||||
@@ -466,9 +466,8 @@ export function RuntimeConfigPage() {
|
|||||||
<table className="w-full table-fixed text-left text-sm">
|
<table className="w-full table-fixed text-left text-sm">
|
||||||
<colgroup>
|
<colgroup>
|
||||||
<col className="w-[22%]" />
|
<col className="w-[22%]" />
|
||||||
<col className="w-[29%]" />
|
<col className="w-[30%]" />
|
||||||
<col className="w-[19%]" />
|
<col />
|
||||||
<col className="w-[24%]" />
|
|
||||||
<col className="w-20" />
|
<col className="w-20" />
|
||||||
</colgroup>
|
</colgroup>
|
||||||
<thead className="sticky top-0 z-10 border-b border-border bg-card text-xs text-muted-foreground">
|
<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">구성요소</th>
|
||||||
<th className="px-5 py-3 font-medium">Key</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">Value</th>
|
||||||
<th className="px-5 py-3 font-medium">등록일</th>
|
|
||||||
<th className="w-16 px-5 py-3 text-right font-medium">관리</th>
|
<th className="w-16 px-5 py-3 text-right font-medium">관리</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="divide-y divide-border">
|
<tbody className="divide-y divide-border">
|
||||||
{variables.length === 0 ? (
|
{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}
|
) : null}
|
||||||
{variables.map((variable) => (
|
{variables.map((variable) => (
|
||||||
<tr key={variable.id} className="hover:bg-muted/20">
|
<tr key={variable.id} className="hover:bg-muted/20">
|
||||||
@@ -494,7 +492,6 @@ export function RuntimeConfigPage() {
|
|||||||
</td>
|
</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 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="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">
|
<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)}>
|
<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" />
|
<Trash2 className="size-4" />
|
||||||
|
|||||||
Reference in New Issue
Block a user