This commit is contained in:
-24
@@ -60,10 +60,6 @@ import {
|
|||||||
AgentInstallStatusPage as AgentInstallStatusRestoredPage,
|
AgentInstallStatusPage as AgentInstallStatusRestoredPage,
|
||||||
AgentRegistrationPage as AgentRegistrationRestoredPage,
|
AgentRegistrationPage as AgentRegistrationRestoredPage,
|
||||||
AgentSshKeysPage as AgentSshKeysRestoredPage,
|
AgentSshKeysPage as AgentSshKeysRestoredPage,
|
||||||
AgentWorkspacePage as AgentWorkspaceRestoredPage,
|
|
||||||
GatewayAgentsPage as GatewayAgentsRestoredPage,
|
|
||||||
GatewayDevicesPage as GatewayDevicesRestoredPage,
|
|
||||||
GatewayStatusPage as GatewayStatusRestoredPage,
|
|
||||||
TailscaleAclPage as TailscaleAclRestoredPage,
|
TailscaleAclPage as TailscaleAclRestoredPage,
|
||||||
TailscaleRoutesPage as TailscaleRoutesRestoredPage,
|
TailscaleRoutesPage as TailscaleRoutesRestoredPage,
|
||||||
TailscaleStatusPage as TailscaleStatusRestoredPage
|
TailscaleStatusPage as TailscaleStatusRestoredPage
|
||||||
@@ -90,9 +86,6 @@ type ViewKey =
|
|||||||
| 'appReleases'
|
| 'appReleases'
|
||||||
| 'appDeployments'
|
| 'appDeployments'
|
||||||
| 'appMigrations'
|
| 'appMigrations'
|
||||||
| 'gatewayStatus'
|
|
||||||
| 'gatewayAgents'
|
|
||||||
| 'gatewayDevices'
|
|
||||||
| 'tailscaleStatus'
|
| 'tailscaleStatus'
|
||||||
| 'tailscaleRoutes'
|
| 'tailscaleRoutes'
|
||||||
| 'tailscaleAcl'
|
| 'tailscaleAcl'
|
||||||
@@ -104,7 +97,6 @@ type ViewKey =
|
|||||||
| 'agentRegistration'
|
| 'agentRegistration'
|
||||||
| 'agentInstallStatus'
|
| 'agentInstallStatus'
|
||||||
| 'agentSshKeys'
|
| 'agentSshKeys'
|
||||||
| 'agentWorkspace'
|
|
||||||
| 'agentCommands'
|
| 'agentCommands'
|
||||||
| 'runtimeConfig'
|
| 'runtimeConfig'
|
||||||
| 'runtimeLogs'
|
| 'runtimeLogs'
|
||||||
@@ -129,9 +121,6 @@ const viewByLabel: Record<string, ViewKey> = {
|
|||||||
'실행 설정': 'runtimeConfig',
|
'실행 설정': 'runtimeConfig',
|
||||||
'배포 관리': 'appDeployments',
|
'배포 관리': 'appDeployments',
|
||||||
마이그레이션: 'appMigrations',
|
마이그레이션: 'appMigrations',
|
||||||
'연결 상태': 'gatewayStatus',
|
|
||||||
'Agent 관리': 'gatewayAgents',
|
|
||||||
'장비 동기화': 'gatewayDevices',
|
|
||||||
'노드 현황': 'tailscaleStatus',
|
'노드 현황': 'tailscaleStatus',
|
||||||
라우트: 'tailscaleRoutes',
|
라우트: 'tailscaleRoutes',
|
||||||
ACL: 'tailscaleAcl',
|
ACL: 'tailscaleAcl',
|
||||||
@@ -143,7 +132,6 @@ const viewByLabel: Record<string, ViewKey> = {
|
|||||||
'장비 등록': 'agentRegistration',
|
'장비 등록': 'agentRegistration',
|
||||||
'설치 상태': 'agentInstallStatus',
|
'설치 상태': 'agentInstallStatus',
|
||||||
'SSH 키 배포': 'agentSshKeys',
|
'SSH 키 배포': 'agentSshKeys',
|
||||||
'Workspace 설정': 'agentWorkspace',
|
|
||||||
'원격 명령': 'agentCommands',
|
'원격 명령': 'agentCommands',
|
||||||
'로그/이벤트': 'runtimeLogs',
|
'로그/이벤트': 'runtimeLogs',
|
||||||
'장비 제어': 'runtimeDeviceControl',
|
'장비 제어': 'runtimeDeviceControl',
|
||||||
@@ -174,9 +162,6 @@ const pathByView: Record<ViewKey, string> = {
|
|||||||
appReleases: '/applications/releases',
|
appReleases: '/applications/releases',
|
||||||
appDeployments: '/applications/deployments',
|
appDeployments: '/applications/deployments',
|
||||||
appMigrations: '/applications/migrations',
|
appMigrations: '/applications/migrations',
|
||||||
gatewayStatus: '/gateway/status',
|
|
||||||
gatewayAgents: '/gateway/agents',
|
|
||||||
gatewayDevices: '/gateway/devices',
|
|
||||||
tailscaleStatus: '/tailscale/status',
|
tailscaleStatus: '/tailscale/status',
|
||||||
tailscaleRoutes: '/tailscale/routes',
|
tailscaleRoutes: '/tailscale/routes',
|
||||||
tailscaleAcl: '/tailscale/acl',
|
tailscaleAcl: '/tailscale/acl',
|
||||||
@@ -188,7 +173,6 @@ const pathByView: Record<ViewKey, string> = {
|
|||||||
agentRegistration: '/agents/registration',
|
agentRegistration: '/agents/registration',
|
||||||
agentInstallStatus: '/agents/install-status',
|
agentInstallStatus: '/agents/install-status',
|
||||||
agentSshKeys: '/agents/ssh-keys',
|
agentSshKeys: '/agents/ssh-keys',
|
||||||
agentWorkspace: '/agents/workspace',
|
|
||||||
agentCommands: '/agents/commands',
|
agentCommands: '/agents/commands',
|
||||||
runtimeConfig: '/applications/runtime-settings',
|
runtimeConfig: '/applications/runtime-settings',
|
||||||
runtimeLogs: '/runtime/logs',
|
runtimeLogs: '/runtime/logs',
|
||||||
@@ -2325,12 +2309,6 @@ function App() {
|
|||||||
return <DeploymentManagementPage />;
|
return <DeploymentManagementPage />;
|
||||||
case 'appMigrations':
|
case 'appMigrations':
|
||||||
return <MigrationManagementPage />;
|
return <MigrationManagementPage />;
|
||||||
case 'gatewayStatus':
|
|
||||||
return <GatewayStatusRestoredPage />;
|
|
||||||
case 'gatewayAgents':
|
|
||||||
return <GatewayAgentsRestoredPage />;
|
|
||||||
case 'gatewayDevices':
|
|
||||||
return <GatewayDevicesRestoredPage />;
|
|
||||||
case 'tailscaleStatus':
|
case 'tailscaleStatus':
|
||||||
return <TailscaleStatusRestoredPage />;
|
return <TailscaleStatusRestoredPage />;
|
||||||
case 'tailscaleRoutes':
|
case 'tailscaleRoutes':
|
||||||
@@ -2353,8 +2331,6 @@ function App() {
|
|||||||
return <AgentInstallStatusRestoredPage />;
|
return <AgentInstallStatusRestoredPage />;
|
||||||
case 'agentSshKeys':
|
case 'agentSshKeys':
|
||||||
return <AgentSshKeysRestoredPage />;
|
return <AgentSshKeysRestoredPage />;
|
||||||
case 'agentWorkspace':
|
|
||||||
return <AgentWorkspaceRestoredPage />;
|
|
||||||
case 'agentCommands':
|
case 'agentCommands':
|
||||||
return <AgentCommandsRestoredPage />;
|
return <AgentCommandsRestoredPage />;
|
||||||
case 'runtimeConfig':
|
case 'runtimeConfig':
|
||||||
|
|||||||
@@ -75,19 +75,9 @@ export const navGroups = [
|
|||||||
{ label: '장비 제어' },
|
{ label: '장비 제어' },
|
||||||
{ label: '설치 상태' },
|
{ label: '설치 상태' },
|
||||||
{ label: 'SSH 키 배포' },
|
{ label: 'SSH 키 배포' },
|
||||||
{ label: 'Workspace 설정' },
|
|
||||||
{ label: '원격 명령' }
|
{ label: '원격 명령' }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: '플랫폼 게이트웨이',
|
|
||||||
icon: ServerCog,
|
|
||||||
children: [
|
|
||||||
{ label: '연결 상태' },
|
|
||||||
{ label: 'Agent 관리' },
|
|
||||||
{ label: '장비 동기화' }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
label: '인프라',
|
label: '인프라',
|
||||||
icon: HardDrive,
|
icon: HardDrive,
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import {
|
|||||||
Megaphone,
|
Megaphone,
|
||||||
Network,
|
Network,
|
||||||
Rocket,
|
Rocket,
|
||||||
ServerCog,
|
|
||||||
ShieldCheck,
|
ShieldCheck,
|
||||||
Users
|
Users
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
@@ -80,19 +79,9 @@ export const navGroups: NavGroup[] = [
|
|||||||
{ label: '장비 제어', path: '/runtime/devices' },
|
{ label: '장비 제어', path: '/runtime/devices' },
|
||||||
{ label: '설치 상태', path: '/agents/install-status' },
|
{ label: '설치 상태', path: '/agents/install-status' },
|
||||||
{ label: 'SSH 키 배포', path: '/agents/ssh-keys' },
|
{ label: 'SSH 키 배포', path: '/agents/ssh-keys' },
|
||||||
{ label: 'Workspace 설정', path: '/agents/workspace' },
|
|
||||||
{ label: '원격 명령', path: '/agents/commands' }
|
{ label: '원격 명령', path: '/agents/commands' }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: '플랫폼 게이트웨이',
|
|
||||||
icon: ServerCog,
|
|
||||||
children: [
|
|
||||||
{ label: '연결 상태', path: '/gateway/status' },
|
|
||||||
{ label: 'Agent 관리', path: '/gateway/agents' },
|
|
||||||
{ label: '장비 동기화', path: '/gateway/devices' }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
label: '인프라',
|
label: '인프라',
|
||||||
icon: HardDrive,
|
icon: HardDrive,
|
||||||
|
|||||||
@@ -879,15 +879,6 @@ export function AgentSshKeysPage() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AgentWorkspacePage() {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<PageTitle eyebrow="Agent" title="Workspace 설정" description="장비별 작업 디렉터리와 동기화 정책을 관리합니다." />
|
|
||||||
<AgentPlaceholder title="Workspace 설정" description="계정별 workspace 경로와 동기화 상태입니다." />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function AgentCommandsPage() {
|
export function AgentCommandsPage() {
|
||||||
const columns = useMemo<Array<DataTableColumn<(typeof runtimeCommands)[number]>>>(
|
const columns = useMemo<Array<DataTableColumn<(typeof runtimeCommands)[number]>>>(
|
||||||
() => [
|
() => [
|
||||||
|
|||||||
@@ -77,6 +77,11 @@ export function DeploymentManagementPage() {
|
|||||||
const [releases, setReleases] = useState<AppRelease[]>([]);
|
const [releases, setReleases] = useState<AppRelease[]>([]);
|
||||||
const [releaseId, setReleaseId] = useState('');
|
const [releaseId, setReleaseId] = useState('');
|
||||||
const [namespace, setNamespace] = useState('');
|
const [namespace, setNamespace] = useState('');
|
||||||
|
const [replicas, setReplicas] = useState('1');
|
||||||
|
const [cpuRequest, setCpuRequest] = useState('100m');
|
||||||
|
const [cpuLimit, setCpuLimit] = useState('500m');
|
||||||
|
const [memoryRequest, setMemoryRequest] = useState('128Mi');
|
||||||
|
const [memoryLimit, setMemoryLimit] = useState('256Mi');
|
||||||
const [updateLauncherAppVersion, setUpdateLauncherAppVersion] = useState(true);
|
const [updateLauncherAppVersion, setUpdateLauncherAppVersion] = useState(true);
|
||||||
const [deployModalOpen, setDeployModalOpen] = useState(false);
|
const [deployModalOpen, setDeployModalOpen] = useState(false);
|
||||||
const [selectedDeployment, setSelectedDeployment] = useState<DeploymentJob | null>(null);
|
const [selectedDeployment, setSelectedDeployment] = useState<DeploymentJob | null>(null);
|
||||||
@@ -158,6 +163,12 @@ export function DeploymentManagementPage() {
|
|||||||
setReleaseId(release.id);
|
setReleaseId(release.id);
|
||||||
setNamespace(release.app.namespace || '');
|
setNamespace(release.app.namespace || '');
|
||||||
}
|
}
|
||||||
|
if (!release && !releaseId && !releases[0]?.app?.namespace) setNamespace('');
|
||||||
|
setReplicas('1');
|
||||||
|
setCpuRequest('100m');
|
||||||
|
setCpuLimit('500m');
|
||||||
|
setMemoryRequest('128Mi');
|
||||||
|
setMemoryLimit('256Mi');
|
||||||
setUpdateLauncherAppVersion(true);
|
setUpdateLauncherAppVersion(true);
|
||||||
setDeployModalOpen(true);
|
setDeployModalOpen(true);
|
||||||
};
|
};
|
||||||
@@ -165,8 +176,22 @@ export function DeploymentManagementPage() {
|
|||||||
const deploy = async () => {
|
const deploy = async () => {
|
||||||
if (deploying) return;
|
if (deploying) return;
|
||||||
setDeploying(true);
|
setDeploying(true);
|
||||||
|
const requestedReplicas = Number.parseInt(replicas, 10);
|
||||||
|
const resources = {
|
||||||
|
...(Number.isFinite(requestedReplicas) && requestedReplicas > 0 ? { replicas: requestedReplicas } : {}),
|
||||||
|
...(cpuRequest.trim() ? { cpuRequest: cpuRequest.trim() } : {}),
|
||||||
|
...(cpuLimit.trim() ? { cpuLimit: cpuLimit.trim() } : {}),
|
||||||
|
...(memoryRequest.trim() ? { memoryRequest: memoryRequest.trim() } : {}),
|
||||||
|
...(memoryLimit.trim() ? { memoryLimit: memoryLimit.trim() } : {})
|
||||||
|
};
|
||||||
try {
|
try {
|
||||||
const result = await createDeployment({ releaseId, environment: 'prod', namespace, updateLauncherAppVersion });
|
const result = await createDeployment({
|
||||||
|
releaseId,
|
||||||
|
environment: 'prod',
|
||||||
|
namespace,
|
||||||
|
updateLauncherAppVersion,
|
||||||
|
resources: Object.keys(resources).length ? resources : undefined
|
||||||
|
});
|
||||||
toast({ type: 'success', message: '배포 작업이 실행되었습니다.' });
|
toast({ type: 'success', message: '배포 작업이 실행되었습니다.' });
|
||||||
setSelectedDeployment(result.deploymentJob);
|
setSelectedDeployment(result.deploymentJob);
|
||||||
setDeployModalOpen(false);
|
setDeployModalOpen(false);
|
||||||
@@ -357,6 +382,60 @@ export function DeploymentManagementPage() {
|
|||||||
{deployableReleases.map((release) => <option key={release.id} value={release.id}>{release.app.name} {release.version}</option>)}
|
{deployableReleases.map((release) => <option key={release.id} value={release.id}>{release.app.name} {release.version}</option>)}
|
||||||
</select>
|
</select>
|
||||||
<input className="h-9 w-full rounded-md border border-input bg-background px-3 text-sm outline-none" value={namespace} disabled={deploying} onChange={(event) => setNamespace(event.target.value)} placeholder="stdo-lms" />
|
<input className="h-9 w-full rounded-md border border-input bg-background px-3 text-sm outline-none" value={namespace} disabled={deploying} onChange={(event) => setNamespace(event.target.value)} placeholder="stdo-lms" />
|
||||||
|
<div className="grid gap-3 md:grid-cols-2">
|
||||||
|
<label className="block">
|
||||||
|
<span className="text-xs font-medium text-muted-foreground">Replicas</span>
|
||||||
|
<input
|
||||||
|
className="mt-2 h-9 w-full rounded-md border border-input bg-background px-3 text-sm outline-none"
|
||||||
|
value={replicas}
|
||||||
|
disabled={deploying}
|
||||||
|
onChange={(event) => setReplicas(event.target.value)}
|
||||||
|
placeholder="1"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label className="block">
|
||||||
|
<span className="text-xs font-medium text-muted-foreground">CPU 요청</span>
|
||||||
|
<input
|
||||||
|
className="mt-2 h-9 w-full rounded-md border border-input bg-background px-3 text-sm outline-none"
|
||||||
|
value={cpuRequest}
|
||||||
|
disabled={deploying}
|
||||||
|
onChange={(event) => setCpuRequest(event.target.value)}
|
||||||
|
placeholder="예: 100m"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className="grid gap-3 md:grid-cols-2">
|
||||||
|
<label className="block">
|
||||||
|
<span className="text-xs font-medium text-muted-foreground">CPU 상한</span>
|
||||||
|
<input
|
||||||
|
className="mt-2 h-9 w-full rounded-md border border-input bg-background px-3 text-sm outline-none"
|
||||||
|
value={cpuLimit}
|
||||||
|
disabled={deploying}
|
||||||
|
onChange={(event) => setCpuLimit(event.target.value)}
|
||||||
|
placeholder="예: 500m"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label className="block">
|
||||||
|
<span className="text-xs font-medium text-muted-foreground">메모리 요청</span>
|
||||||
|
<input
|
||||||
|
className="mt-2 h-9 w-full rounded-md border border-input bg-background px-3 text-sm outline-none"
|
||||||
|
value={memoryRequest}
|
||||||
|
disabled={deploying}
|
||||||
|
onChange={(event) => setMemoryRequest(event.target.value)}
|
||||||
|
placeholder="예: 256Mi"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<label className="block">
|
||||||
|
<span className="text-xs font-medium text-muted-foreground">메모리 상한</span>
|
||||||
|
<input
|
||||||
|
className="mt-2 h-9 w-full rounded-md border border-input bg-background px-3 text-sm outline-none"
|
||||||
|
value={memoryLimit}
|
||||||
|
disabled={deploying}
|
||||||
|
onChange={(event) => setMemoryLimit(event.target.value)}
|
||||||
|
placeholder="예: 512Mi"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
<label className="flex items-center gap-2 rounded-md border border-border bg-muted/20 px-3 py-2 text-sm text-foreground">
|
<label className="flex items-center gap-2 rounded-md border border-border bg-muted/20 px-3 py-2 text-sm text-foreground">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
|
|||||||
@@ -12,6 +12,14 @@ import type {
|
|||||||
} from '../../types/application';
|
} from '../../types/application';
|
||||||
import type { ApiAppComponent } from '../../types/runtime';
|
import type { ApiAppComponent } from '../../types/runtime';
|
||||||
|
|
||||||
|
export type DeploymentResourceLimits = {
|
||||||
|
replicas?: number;
|
||||||
|
cpuRequest?: string;
|
||||||
|
cpuLimit?: string;
|
||||||
|
memoryRequest?: string;
|
||||||
|
memoryLimit?: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type GiteaCommit = {
|
export type GiteaCommit = {
|
||||||
sha: string;
|
sha: string;
|
||||||
commit?: {
|
commit?: {
|
||||||
@@ -230,7 +238,13 @@ export function listDeploymentsPage(options: { appKey?: string; page: number; li
|
|||||||
return apiRequest<{ deployments: DeploymentJob[]; total: number; page: number; limit: number; deployedReleaseIds?: string[] }>(`/apps/deployments?${params.toString()}`);
|
return apiRequest<{ deployments: DeploymentJob[]; total: number; page: number; limit: number; deployedReleaseIds?: string[] }>(`/apps/deployments?${params.toString()}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createDeployment(input: { releaseId: string; environment: string; namespace: string; updateLauncherAppVersion?: boolean }) {
|
export function createDeployment(input: {
|
||||||
|
releaseId: string;
|
||||||
|
environment: string;
|
||||||
|
namespace: string;
|
||||||
|
updateLauncherAppVersion?: boolean;
|
||||||
|
resources?: DeploymentResourceLimits;
|
||||||
|
}) {
|
||||||
return apiRequest<{ deploymentJob: DeploymentJob }>('/apps/deployments', {
|
return apiRequest<{ deploymentJob: DeploymentJob }>('/apps/deployments', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify(input)
|
body: JSON.stringify(input)
|
||||||
|
|||||||
@@ -36,6 +36,11 @@ export function LauncherVersionsPage() {
|
|||||||
const [deployOpen, setDeployOpen] = useState(false);
|
const [deployOpen, setDeployOpen] = useState(false);
|
||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false);
|
||||||
const [deploying, setDeploying] = useState(false);
|
const [deploying, setDeploying] = useState(false);
|
||||||
|
const [deployReplicas, setDeployReplicas] = useState('1');
|
||||||
|
const [deployCpuRequest, setDeployCpuRequest] = useState('100m');
|
||||||
|
const [deployCpuLimit, setDeployCpuLimit] = useState('500m');
|
||||||
|
const [deployMemoryRequest, setDeployMemoryRequest] = useState('128Mi');
|
||||||
|
const [deployMemoryLimit, setDeployMemoryLimit] = useState('256Mi');
|
||||||
const [releaseForm, setReleaseForm] = useState({
|
const [releaseForm, setReleaseForm] = useState({
|
||||||
buildJobId: '',
|
buildJobId: '',
|
||||||
version: '',
|
version: '',
|
||||||
@@ -128,7 +133,16 @@ export function LauncherVersionsPage() {
|
|||||||
const deployBackend = () => {
|
const deployBackend = () => {
|
||||||
if (!latestBackendBuild?.downloadUrl) return;
|
if (!latestBackendBuild?.downloadUrl) return;
|
||||||
setDeploying(true);
|
setDeploying(true);
|
||||||
deployLauncherBackend(latestBackendBuild.downloadUrl)
|
const requestedReplicas = Number.parseInt(deployReplicas, 10);
|
||||||
|
const resources = {
|
||||||
|
...(Number.isFinite(requestedReplicas) && requestedReplicas > 0 ? { replicas: requestedReplicas } : {}),
|
||||||
|
...(deployCpuRequest.trim() ? { cpuRequest: deployCpuRequest.trim() } : {}),
|
||||||
|
...(deployCpuLimit.trim() ? { cpuLimit: deployCpuLimit.trim() } : {}),
|
||||||
|
...(deployMemoryRequest.trim() ? { memoryRequest: deployMemoryRequest.trim() } : {}),
|
||||||
|
...(deployMemoryLimit.trim() ? { memoryLimit: deployMemoryLimit.trim() } : {})
|
||||||
|
};
|
||||||
|
|
||||||
|
deployLauncherBackend(latestBackendBuild.downloadUrl, Object.keys(resources).length ? resources : undefined)
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
setDeployResult(result);
|
setDeployResult(result);
|
||||||
toast({ type: 'success', message: '런처 백엔드를 배포했습니다.' });
|
toast({ type: 'success', message: '런처 백엔드를 배포했습니다.' });
|
||||||
@@ -141,6 +155,15 @@ export function LauncherVersionsPage() {
|
|||||||
.finally(() => setDeploying(false));
|
.finally(() => setDeploying(false));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const openBackendDeploy = () => {
|
||||||
|
setDeployReplicas('1');
|
||||||
|
setDeployCpuRequest('100m');
|
||||||
|
setDeployCpuLimit('500m');
|
||||||
|
setDeployMemoryRequest('128Mi');
|
||||||
|
setDeployMemoryLimit('256Mi');
|
||||||
|
setDeployOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageTitle
|
<PageTitle
|
||||||
@@ -159,7 +182,7 @@ export function LauncherVersionsPage() {
|
|||||||
릴리즈 등록
|
릴리즈 등록
|
||||||
</Button>
|
</Button>
|
||||||
) : (
|
) : (
|
||||||
<Button className="h-8 px-3 text-[13px]" onClick={() => setDeployOpen(true)} disabled={!latestBackendBuild?.downloadUrl}>
|
<Button className="h-8 px-3 text-[13px]" onClick={openBackendDeploy} disabled={!latestBackendBuild?.downloadUrl}>
|
||||||
<Play className="size-4" />
|
<Play className="size-4" />
|
||||||
백엔드 배포
|
백엔드 배포
|
||||||
</Button>
|
</Button>
|
||||||
@@ -345,6 +368,60 @@ export function LauncherVersionsPage() {
|
|||||||
<p className="font-medium">최신 성공 빌드 이미지로 런처 백엔드를 배포합니다.</p>
|
<p className="font-medium">최신 성공 빌드 이미지로 런처 백엔드를 배포합니다.</p>
|
||||||
<p className="mt-1 text-xs leading-5 text-muted-foreground">Deployment 이미지가 교체되고 rollout 완료까지 대기합니다.</p>
|
<p className="mt-1 text-xs leading-5 text-muted-foreground">Deployment 이미지가 교체되고 rollout 완료까지 대기합니다.</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="grid gap-3 md:grid-cols-2">
|
||||||
|
<label className="block">
|
||||||
|
<span className="text-xs font-medium text-muted-foreground">Replicas</span>
|
||||||
|
<input
|
||||||
|
className="mt-2 h-9 w-full rounded-md border border-input bg-background px-3 text-sm outline-none"
|
||||||
|
value={deployReplicas}
|
||||||
|
disabled={deploying}
|
||||||
|
onChange={(event) => setDeployReplicas(event.target.value)}
|
||||||
|
placeholder="1"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label className="block">
|
||||||
|
<span className="text-xs font-medium text-muted-foreground">CPU 요청</span>
|
||||||
|
<input
|
||||||
|
className="mt-2 h-9 w-full rounded-md border border-input bg-background px-3 text-sm outline-none"
|
||||||
|
value={deployCpuRequest}
|
||||||
|
disabled={deploying}
|
||||||
|
onChange={(event) => setDeployCpuRequest(event.target.value)}
|
||||||
|
placeholder="예: 100m"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className="grid gap-3 md:grid-cols-2">
|
||||||
|
<label className="block">
|
||||||
|
<span className="text-xs font-medium text-muted-foreground">CPU 상한</span>
|
||||||
|
<input
|
||||||
|
className="mt-2 h-9 w-full rounded-md border border-input bg-background px-3 text-sm outline-none"
|
||||||
|
value={deployCpuLimit}
|
||||||
|
disabled={deploying}
|
||||||
|
onChange={(event) => setDeployCpuLimit(event.target.value)}
|
||||||
|
placeholder="예: 500m"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label className="block">
|
||||||
|
<span className="text-xs font-medium text-muted-foreground">메모리 요청</span>
|
||||||
|
<input
|
||||||
|
className="mt-2 h-9 w-full rounded-md border border-input bg-background px-3 text-sm outline-none"
|
||||||
|
value={deployMemoryRequest}
|
||||||
|
disabled={deploying}
|
||||||
|
onChange={(event) => setDeployMemoryRequest(event.target.value)}
|
||||||
|
placeholder="예: 256Mi"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<label className="block">
|
||||||
|
<span className="text-xs font-medium text-muted-foreground">메모리 상한</span>
|
||||||
|
<input
|
||||||
|
className="mt-2 h-9 w-full rounded-md border border-input bg-background px-3 text-sm outline-none"
|
||||||
|
value={deployMemoryLimit}
|
||||||
|
disabled={deploying}
|
||||||
|
onChange={(event) => setDeployMemoryLimit(event.target.value)}
|
||||||
|
placeholder="예: 512Mi"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
<div className="rounded-md border border-border bg-background p-4">
|
<div className="rounded-md border border-border bg-background p-4">
|
||||||
<p className="text-xs text-muted-foreground">배포 이미지</p>
|
<p className="text-xs text-muted-foreground">배포 이미지</p>
|
||||||
<p className="mt-2 break-all font-mono text-xs font-medium">{latestBackendBuild?.downloadUrl || '-'}</p>
|
<p className="mt-2 break-all font-mono text-xs font-medium">{latestBackendBuild?.downloadUrl || '-'}</p>
|
||||||
|
|||||||
@@ -63,6 +63,14 @@ export type LauncherBackendDeployResult = {
|
|||||||
logs: string;
|
logs: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type DeploymentResourceLimits = {
|
||||||
|
replicas?: number;
|
||||||
|
cpuRequest?: string;
|
||||||
|
cpuLimit?: string;
|
||||||
|
memoryRequest?: string;
|
||||||
|
memoryLimit?: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type LauncherBuildEnvVariable = {
|
export type LauncherBuildEnvVariable = {
|
||||||
key: string;
|
key: string;
|
||||||
value: string;
|
value: string;
|
||||||
@@ -281,10 +289,10 @@ export function getLauncherBackendDeployment() {
|
|||||||
return apiRequest<LauncherBackendDeployment>('/launcher/backend-deployment');
|
return apiRequest<LauncherBackendDeployment>('/launcher/backend-deployment');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deployLauncherBackend(image: string) {
|
export function deployLauncherBackend(image: string, resources?: DeploymentResourceLimits) {
|
||||||
return apiRequest<LauncherBackendDeployResult>('/launcher/backend-deployment', {
|
return apiRequest<LauncherBackendDeployResult>('/launcher/backend-deployment', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify({ image })
|
body: JSON.stringify({ image, resources: resources && Object.keys(resources).length ? resources : undefined })
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user