From ac075a1fea569d67218884d35093b79d346cd437 Mon Sep 17 00:00:00 2001 From: chyoo Date: Thu, 9 Jul 2026 14:26:43 +0900 Subject: [PATCH] improve app registration and component service ports --- src/pages/applications/AppManagementPage.tsx | 72 +++++-- .../applications/ReleaseManagementPage.tsx | 29 ++- .../applications/RepositoryManagementPage.tsx | 2 +- src/pages/applications/application-api.ts | 2 +- src/pages/launcher/LauncherAppsPage.tsx | 2 +- src/pages/launcher/LauncherVersionsPage.tsx | 6 +- src/pages/operations/AdminManagementPage.tsx | 14 +- src/pages/operations/LicensesPage.tsx | 10 +- src/pages/operations/NoticesPage.tsx | 8 +- src/pages/operations/OrganizationsPage.tsx | 9 +- src/pages/operations/UserSessionsPage.tsx | 179 +++++++++++++++++- src/pages/operations/operation-utils.tsx | 4 +- src/pages/operations/operations-api.ts | 17 +- src/pages/runtime/RuntimeConfigPage.tsx | 6 +- src/types/runtime.ts | 1 + 15 files changed, 301 insertions(+), 60 deletions(-) diff --git a/src/pages/applications/AppManagementPage.tsx b/src/pages/applications/AppManagementPage.tsx index 8eee3d4..e88d0a8 100644 --- a/src/pages/applications/AppManagementPage.tsx +++ b/src/pages/applications/AppManagementPage.tsx @@ -15,6 +15,7 @@ type ComponentPortForm = { name: string; port: string; targetPort: string; + nodePort: string; protocol: string; }; @@ -29,6 +30,7 @@ const emptyComponentPort = (): ComponentPortForm => ({ name: '', port: '', targetPort: '', + nodePort: '', protocol: 'TCP' }); @@ -252,6 +254,7 @@ function componentPortForms(component: ApiAppComponent): ComponentPortForm[] { name: port.name ?? '', port: port.port ? String(port.port) : '', targetPort: port.targetPort ? String(port.targetPort) : '', + nodePort: port.nodePort ? String(port.nodePort) : '', protocol: port.protocol ?? 'TCP' })); } @@ -261,6 +264,7 @@ function componentPortForms(component: ApiAppComponent): ComponentPortForm[] { name: 'http', port: String(component.port), targetPort: String(component.targetPort ?? component.port), + nodePort: '', protocol: 'TCP' }]; } @@ -271,7 +275,20 @@ function componentPortForms(component: ApiAppComponent): ComponentPortForm[] { function formatComponentPorts(component: ApiAppComponent) { const ports = componentPortForms(component).filter((port) => port.port); if (ports.length === 0) return '-'; - return ports.map((port) => `${port.name || 'port'} ${port.port}:${port.targetPort || port.port}`).join(', '); + return ports.map((port) => { + const nodePort = port.nodePort ? ` / node ${port.nodePort}` : ''; + return `${port.name || 'port'} ${port.port}:${port.targetPort || port.port}${nodePort}`; + }).join(', '); +} + +function appKeyFromName(name: string) { + return name + .toLowerCase() + .trim() + .replace(/\s+/g, '-') + .replace(/[^a-z0-9-]/g, '') + .replace(/-+/g, '-') + .replace(/^-|-$/g, ''); } export function AppManagementPage() { @@ -508,6 +525,13 @@ export function AppManagementPage() { }); }; + const changeNewAppName = (name: string) => { + const appKey = appKeyFromName(name); + setNewAppName(name); + setNewAppKey(appKey); + setNewAppNamespace(appKey ? `stdo-${appKey}` : ''); + }; + const createApp = () => { if (!newAppKey.trim() || !newAppName.trim()) return; @@ -603,6 +627,7 @@ export function AppManagementPage() { name: item.name.trim() || undefined, port: item.port ? Number(item.port) : undefined, targetPort: item.targetPort ? Number(item.targetPort) : item.port ? Number(item.port) : undefined, + nodePort: componentServiceType === 'NodePort' && item.nodePort ? Number(item.nodePort) : undefined, protocol: item.protocol || 'TCP' })) .filter((item) => item.port); @@ -981,7 +1006,7 @@ export function AppManagementPage() { title="앱 구성요소" description="앱을 구성하는 실행 요소와 관련 리소스를 관리합니다." action={ - @@ -1170,20 +1195,20 @@ export function AppManagementPage() {
-

앱 그룹 등록

+

앱 등록

{componentPorts.map((item, index) => ( -
+
setComponentPorts((prev) => prev.map((port, portIndex) => portIndex === index ? { ...port, name: event.target.value } : port))} @@ -1366,6 +1399,17 @@ export function AppManagementPage() { className="h-9 min-w-0 rounded-md border border-border bg-background px-3 text-sm outline-none focus:border-primary" placeholder="컨테이너 포트" /> + {componentServiceType === 'NodePort' ? ( + setComponentPorts((prev) => prev.map((port, portIndex) => portIndex === index ? { ...port, nodePort: event.target.value } : port))} + className="h-9 min-w-0 rounded-md border border-border bg-background px-3 text-sm outline-none focus:border-primary" + placeholder="노드 포트" + /> + ) : null} { - selectApp(event.target.value); - const appBuilds = candidateBuilds.filter((build) => build.component.app.id === event.target.value); - setSelectedBuildIds(latestBuildIdsByComponent(appBuilds)); - setVersion(newestBuildVersion(appBuilds)); - }}> - {candidateGroups.map((group) => )} - - setVersion(event.target.value)} placeholder="버전 예: 0.1.1" /> + +
선택한 develop 커밋을 release로 승격한 뒤 release 브랜치에서 다시 빌드합니다.
-
+
+

빌드 선택 *

+
{modalBuilds.length === 0 ? (
등록 가능한 새 빌드 결과가 없습니다. @@ -492,6 +500,7 @@ export function ReleaseManagementPage() { ); })} +
diff --git a/src/pages/applications/RepositoryManagementPage.tsx b/src/pages/applications/RepositoryManagementPage.tsx index d458dcf..5e355f9 100644 --- a/src/pages/applications/RepositoryManagementPage.tsx +++ b/src/pages/applications/RepositoryManagementPage.tsx @@ -381,7 +381,7 @@ export function RepositoryManagementPage() {