This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { ChevronLeft, ChevronRight, MoreHorizontal, Play, RotateCcw } from 'lucide-react';
|
import { ChevronLeft, ChevronRight, MoreHorizontal, Play, Plus, RotateCcw, Trash2 } from 'lucide-react';
|
||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import { Badge, Button, Card, CardHeader, Modal } from '../../components/ui';
|
import { Badge, Button, Card, CardHeader, Modal } from '../../components/ui';
|
||||||
import { useToast } from '../../components/toast';
|
import { useToast } from '../../components/toast';
|
||||||
@@ -8,10 +8,13 @@ import { LoadingModal, useMinimumLoading } from '../operations/operation-utils';
|
|||||||
import {
|
import {
|
||||||
createLauncherBuildJob,
|
createLauncherBuildJob,
|
||||||
getLauncherSource,
|
getLauncherSource,
|
||||||
|
listLauncherBuildEnv,
|
||||||
listLauncherBuildJobs,
|
listLauncherBuildJobs,
|
||||||
listLauncherVersions,
|
listLauncherVersions,
|
||||||
|
saveLauncherBuildEnv,
|
||||||
type LauncherSource,
|
type LauncherSource,
|
||||||
type LauncherBuildJob,
|
type LauncherBuildJob,
|
||||||
|
type LauncherBuildEnvVariable,
|
||||||
type LauncherVersion
|
type LauncherVersion
|
||||||
} from './launcher-api';
|
} from './launcher-api';
|
||||||
|
|
||||||
@@ -80,6 +83,8 @@ export function LauncherBuildPage() {
|
|||||||
const [branch, setBranch] = useState('release');
|
const [branch, setBranch] = useState('release');
|
||||||
const [commitSha, setCommitSha] = useState('');
|
const [commitSha, setCommitSha] = useState('');
|
||||||
const [jobs, setJobs] = useState<LauncherBuildJob[]>([]);
|
const [jobs, setJobs] = useState<LauncherBuildJob[]>([]);
|
||||||
|
const [buildEnv, setBuildEnv] = useState<LauncherBuildEnvVariable[]>([]);
|
||||||
|
const [buildEnvSaving, setBuildEnvSaving] = useState(false);
|
||||||
const [selectedJob, setSelectedJob] = useState<LauncherBuildJob | null>(null);
|
const [selectedJob, setSelectedJob] = useState<LauncherBuildJob | null>(null);
|
||||||
const [confirmOpen, setConfirmOpen] = useState(false);
|
const [confirmOpen, setConfirmOpen] = useState(false);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
@@ -104,11 +109,12 @@ export function LauncherBuildPage() {
|
|||||||
|
|
||||||
const refresh = () => {
|
const refresh = () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
Promise.all([listLauncherVersions(), listLauncherBuildJobs(), getLauncherSource()])
|
Promise.all([listLauncherVersions(), listLauncherBuildJobs(), getLauncherSource(), listLauncherBuildEnv()])
|
||||||
.then(([nextVersions, nextJobs, nextSource]) => {
|
.then(([nextVersions, nextJobs, nextSource, nextBuildEnv]) => {
|
||||||
setVersions(nextVersions);
|
setVersions(nextVersions);
|
||||||
setJobs(nextJobs);
|
setJobs(nextJobs);
|
||||||
setSource(nextSource.source);
|
setSource(nextSource.source);
|
||||||
|
setBuildEnv(nextBuildEnv.variables ?? []);
|
||||||
const nextVersion = nextVersions.find((item) => item.isActive) ?? nextVersions[0];
|
const nextVersion = nextVersions.find((item) => item.isActive) ?? nextVersions[0];
|
||||||
setVersion(nextVersion?.version ?? '');
|
setVersion(nextVersion?.version ?? '');
|
||||||
setBranch(target === 'launcher-backend' ? nextSource.source.backend?.branch ?? 'release' : nextSource.source.client?.branch ?? nextVersion?.branch ?? 'release');
|
setBranch(target === 'launcher-backend' ? nextSource.source.backend?.branch ?? 'release' : nextSource.source.client?.branch ?? nextVersion?.branch ?? 'release');
|
||||||
@@ -181,6 +187,33 @@ export function LauncherBuildPage() {
|
|||||||
.finally(() => setLoading(false));
|
.finally(() => setLoading(false));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const updateBuildEnv = (index: number, patch: Partial<LauncherBuildEnvVariable>) => {
|
||||||
|
setBuildEnv((prev) => prev.map((variable, variableIndex) => (
|
||||||
|
variableIndex === index ? { ...variable, ...patch } : variable
|
||||||
|
)));
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeBuildEnv = (index: number) => {
|
||||||
|
setBuildEnv((prev) => prev.filter((_, variableIndex) => variableIndex !== index));
|
||||||
|
};
|
||||||
|
|
||||||
|
const persistBuildEnv = () => {
|
||||||
|
setBuildEnvSaving(true);
|
||||||
|
saveLauncherBuildEnv(
|
||||||
|
buildEnv
|
||||||
|
.map((variable) => ({ ...variable, key: variable.key.trim() }))
|
||||||
|
.filter((variable) => variable.key)
|
||||||
|
)
|
||||||
|
.then((result) => {
|
||||||
|
setBuildEnv(result.variables ?? []);
|
||||||
|
toast({ type: 'success', title: '빌드 환경변수 저장', message: '런처 클라이언트 빌드 환경변수를 저장했습니다.' });
|
||||||
|
})
|
||||||
|
.catch((error: Error) => {
|
||||||
|
toast({ type: 'error', title: '빌드 환경변수 저장 실패', message: error.message });
|
||||||
|
})
|
||||||
|
.finally(() => setBuildEnvSaving(false));
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageTitle
|
<PageTitle
|
||||||
@@ -232,7 +265,49 @@ export function LauncherBuildPage() {
|
|||||||
<p className="mt-2 text-xs text-muted-foreground">Repository</p>
|
<p className="mt-2 text-xs text-muted-foreground">Repository</p>
|
||||||
<p className="mt-1 break-all font-mono text-xs text-foreground">{backendSource?.repository || '-'}</p>
|
<p className="mt-1 break-all font-mono text-xs text-foreground">{backendSource?.repository || '-'}</p>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : (
|
||||||
|
<div className="rounded-md border border-border bg-background p-3">
|
||||||
|
<div className="flex items-start justify-between gap-3">
|
||||||
|
<div>
|
||||||
|
<p className="text-xs font-medium text-muted-foreground">클라이언트 빌드 환경변수</p>
|
||||||
|
<p className="mt-1 text-xs leading-5 text-muted-foreground">Vite 빌드 시점에 주입되는 값입니다. 저장 후 실행한 빌드부터 반영됩니다.</p>
|
||||||
|
</div>
|
||||||
|
<Button variant="outline" className="h-8 px-3 text-[13px]" onClick={persistBuildEnv} disabled={buildEnvSaving}>
|
||||||
|
{buildEnvSaving ? '저장 중' : '저장'}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className="mt-3 space-y-2">
|
||||||
|
{buildEnv.map((variable, index) => (
|
||||||
|
<div key={`${variable.key}-${index}`} className="grid gap-2 md:grid-cols-[minmax(0,0.9fr)_minmax(0,1.1fr)_auto]">
|
||||||
|
<input
|
||||||
|
className="h-9 rounded-md border border-input bg-background px-3 font-mono text-xs outline-none focus:ring-2 focus:ring-ring"
|
||||||
|
value={variable.key}
|
||||||
|
onChange={(event) => updateBuildEnv(index, { key: event.target.value })}
|
||||||
|
placeholder="VITE_ATNMY_STDO_API_BASE_URL"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
className="h-9 rounded-md border border-input bg-background px-3 font-mono text-xs outline-none focus:ring-2 focus:ring-ring"
|
||||||
|
value={variable.value}
|
||||||
|
onChange={(event) => updateBuildEnv(index, { value: event.target.value })}
|
||||||
|
placeholder="https://portal-api.autonomystudio.net"
|
||||||
|
/>
|
||||||
|
<Button variant="outline" className="size-9 px-0" title="삭제" aria-label="삭제" onClick={() => removeBuildEnv(index)}>
|
||||||
|
<Trash2 className="size-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
{buildEnv.length === 0 ? <p className="rounded-md border border-dashed border-border px-3 py-4 text-center text-xs text-muted-foreground">등록된 빌드 환경변수가 없습니다.</p> : null}
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
className="mt-3 h-8 px-3 text-[13px]"
|
||||||
|
onClick={() => setBuildEnv((prev) => [...prev, { key: '', value: '', isSecret: false }])}
|
||||||
|
>
|
||||||
|
<Plus className="size-4" />
|
||||||
|
추가
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,12 @@ export type LauncherBuildJob = {
|
|||||||
updatedAt?: string;
|
updatedAt?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type LauncherBuildEnvVariable = {
|
||||||
|
key: string;
|
||||||
|
value: string;
|
||||||
|
isSecret: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
export type LauncherSource = {
|
export type LauncherSource = {
|
||||||
repository?: string | null;
|
repository?: string | null;
|
||||||
branch?: string | null;
|
branch?: string | null;
|
||||||
@@ -193,6 +199,17 @@ export function listLauncherBuildJobs() {
|
|||||||
return apiRequest<LauncherBuildJob[]>('/launcher/builds');
|
return apiRequest<LauncherBuildJob[]>('/launcher/builds');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function listLauncherBuildEnv() {
|
||||||
|
return apiRequest<{ variables: LauncherBuildEnvVariable[] }>('/launcher/build-env');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function saveLauncherBuildEnv(variables: LauncherBuildEnvVariable[]) {
|
||||||
|
return apiRequest<{ variables: LauncherBuildEnvVariable[] }>('/launcher/build-env', {
|
||||||
|
method: 'PUT',
|
||||||
|
body: JSON.stringify({ variables })
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function createLauncherBuildJob(input: {
|
export function createLauncherBuildJob(input: {
|
||||||
target?: string;
|
target?: string;
|
||||||
repository?: string;
|
repository?: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user