diff --git a/front/assets/js/common/api/services/csproles_api.js b/front/assets/js/common/api/services/csproles_api.js index ab30c3f6..8ff1d8c8 100644 --- a/front/assets/js/common/api/services/csproles_api.js +++ b/front/assets/js/common/api/services/csproles_api.js @@ -190,3 +190,46 @@ export async function getCspProviders() { return response.data.responseData; } +// ===== 동기화 API Functions ===== + +// CSP Role 동기화 +export async function syncCspRoles(provider = null) { + const controller = "/api/mc-iam-manager/SyncCspRoles"; + const data = { + Request: { + provider: provider + } + }; + const response = await mockAPIPost(controller, data); + return response.data.responseData; +} + +// 정책 동기화 +export async function syncPolicies(roleId = null) { + const controller = "/api/mc-iam-manager/SyncPolicies"; + const data = { + Request: { + roleId: roleId + } + }; + const response = await mockAPIPost(controller, data); + return response.data.responseData; +} + +// CSP Policy 업데이트 +export async function updateCspPolicy(policyId, policyData) { + const controller = "/api/mc-iam-manager/UpdateCspPolicy"; + const data = { + pathParams: { + policyId: policyId.toString() + }, + Request: { + name: policyData.name, + description: policyData.description, + document: policyData.document + } + }; + const response = await mockAPIPost(controller, data); + return response.data.responseData; +} + diff --git a/front/assets/js/common/api/services/csproles_mock_data.js b/front/assets/js/common/api/services/csproles_mock_data.js index 6c8496b7..f6963f05 100644 --- a/front/assets/js/common/api/services/csproles_mock_data.js +++ b/front/assets/js/common/api/services/csproles_mock_data.js @@ -308,6 +308,15 @@ export function handleMockAPIRequest(controller, data = null) { case "/api/mc-iam-manager/GetCspProviders": result = handleGetCspProviders(); break; + case "/api/mc-iam-manager/SyncCspRoles": + result = handleSyncCspRoles(data); + break; + case "/api/mc-iam-manager/SyncPolicies": + result = handleSyncPolicies(data); + break; + case "/api/mc-iam-manager/UpdateCspPolicy": + result = handleUpdateCspPolicy(data); + break; default: result = { error: "Unknown controller" }; } @@ -558,6 +567,105 @@ function handleGetCspProviders() { })); } +// CSP Role 동기화 +function handleSyncCspRoles(data) { + const request = data?.Request || {}; + const provider = request.provider; + + // 동기화 시뮬레이션 - 실제로는 외부 CSP에서 최신 역할 목록을 가져옴 + const syncRoles = generateMockCspRoles(); + + if (provider) { + const filteredRoles = syncRoles.filter(role => role.provider === provider); + // 기존 역할과 병합 (중복 제거) + const existingRoleIds = mockData.roles.map(r => r.id); + const newRoles = filteredRoles.filter(role => !existingRoleIds.includes(role.id)); + mockData.roles.push(...newRoles); + + return { + success: true, + message: `Synced ${newRoles.length} new roles for ${provider}`, + syncedRoles: newRoles.length, + totalRoles: mockData.roles.filter(r => r.provider === provider).length + }; + } else { + // 모든 Provider 동기화 + const existingRoleIds = mockData.roles.map(r => r.id); + const newRoles = syncRoles.filter(role => !existingRoleIds.includes(role.id)); + mockData.roles.push(...newRoles); + + return { + success: true, + message: `Synced ${newRoles.length} new roles across all providers`, + syncedRoles: newRoles.length, + totalRoles: mockData.roles.length + }; + } +} + +// 정책 동기화 +function handleSyncPolicies(data) { + const request = data?.Request || {}; + const roleId = request.roleId; + + // 동기화 시뮬레이션 - 실제로는 외부 CSP에서 최신 정책 목록을 가져옴 + const syncPolicies = generateMockCspPolicies(); + + if (roleId) { + // 특정 역할의 정책 동기화 + const role = mockData.roles.find(r => r.id === roleId); + if (!role) { + throw new Error(`CSP Role with ID ${roleId} not found`); + } + + const rolePolicies = syncPolicies.filter(policy => policy.provider === role.provider); + const existingPolicyIds = mockData.policies.map(p => p.id); + const newPolicies = rolePolicies.filter(policy => !existingPolicyIds.includes(policy.id)); + mockData.policies.push(...newPolicies); + + return { + success: true, + message: `Synced ${newPolicies.length} new policies for role ${roleId}`, + syncedPolicies: newPolicies.length, + roleId: roleId + }; + } else { + // 모든 정책 동기화 + const existingPolicyIds = mockData.policies.map(p => p.id); + const newPolicies = syncPolicies.filter(policy => !existingPolicyIds.includes(policy.id)); + mockData.policies.push(...newPolicies); + + return { + success: true, + message: `Synced ${newPolicies.length} new policies`, + syncedPolicies: newPolicies.length, + totalPolicies: mockData.policies.length + }; + } +} + +// CSP Policy 업데이트 +function handleUpdateCspPolicy(data) { + const policyId = data?.pathParams?.policyId; + const request = data?.Request || {}; + + const policyIndex = mockData.policies.findIndex(p => p.id === policyId); + if (policyIndex === -1) { + throw new Error(`CSP Policy with ID ${policyId} not found`); + } + + const updatedPolicy = { + ...mockData.policies[policyIndex], + name: request.name || mockData.policies[policyIndex].name, + description: request.description || mockData.policies[policyIndex].description, + document: request.document || mockData.policies[policyIndex].document, + last_modified: new Date().toISOString() + }; + + mockData.policies[policyIndex] = updatedPolicy; + return { success: true, policy: updatedPolicy }; +} + // ===== Utility Functions ===== // 목데이터 초기화 (테스트용) diff --git a/front/assets/js/common/api/services/mci_api.js b/front/assets/js/common/api/services/mci_api.js index 379f507f..365501d8 100644 --- a/front/assets/js/common/api/services/mci_api.js +++ b/front/assets/js/common/api/services/mci_api.js @@ -325,8 +325,8 @@ export async function searchImage(nsId, searchParams) { request: { includeDeprecatedImage: searchParams.includeDeprecatedImage || false, isGPUImage: searchParams.isGPUImage || false, - isKubernetesImage: searchParams.isKubernetesImage || false, - isRegisteredByAsset: searchParams.isRegisteredByAsset || false, + // isKubernetesImage: searchParams.isKubernetesImage || false, + // isRegisteredByAsset: searchParams.isRegisteredByAsset || false, osArchitecture: searchParams.osArchitecture || "x86_64", osType: searchParams.osType || "ubuntu 22.04", providerName: searchParams.providerName || "", @@ -764,7 +764,7 @@ export async function createPolicy(nsId, mciId, policy) { alert("Project has not set") return; } - +console.log("policy", policy) let data = { pathParams: { nsId: nsId, diff --git a/front/assets/js/common/api/services/remotecmd_api.js b/front/assets/js/common/api/services/remotecmd_api.js index fea6f699..b717751d 100644 --- a/front/assets/js/common/api/services/remotecmd_api.js +++ b/front/assets/js/common/api/services/remotecmd_api.js @@ -133,7 +133,7 @@ async function processCommand(nsid, resourceId, targetId, command, term, callbac const stdout = response.stdout; const stderr = response.stderr; - if (callErr) { + if (callErr && Object.keys(callErr).length > 0) { const formattedError = JSON.stringify(callErr, null, 2); writeAutoWrap(term, " > connect Error: \x1b[1m\x1b[31m" + formattedError + "\x1b[0m"); callback({ error: callErr }); diff --git a/front/assets/js/pages/operation/manage/mci.js b/front/assets/js/pages/operation/manage/mci.js index 98b4c7d6..cf77011e 100644 --- a/front/assets/js/pages/operation/manage/mci.js +++ b/front/assets/js/pages/operation/manage/mci.js @@ -515,6 +515,11 @@ export function handleCheck(type, id) { if (div.classList.contains("active")) { webconsolejs["partials/layout/navigatePages"].toggleElement(div); } + // Server Info도 닫기 + const serverInfoDiv = document.getElementById("subGroup_vm_info"); + if (serverInfoDiv && serverInfoDiv.classList.contains("active")) { + webconsolejs["partials/layout/navigatePages"].toggleElement(serverInfoDiv); + } } else { // 다른 항목 선택 - 기존 선택 해제 후 새 항목 선택 if (selectedVmGroupId && selectedVmGroupId !== id) { @@ -522,6 +527,11 @@ export function handleCheck(type, id) { } selectedVmGroupId = id; currentSubGroupId = id; + // Server Info 닫기 (다른 항목 선택 시) + const serverInfoDiv = document.getElementById("subGroup_vm_info"); + if (serverInfoDiv && serverInfoDiv.classList.contains("active")) { + webconsolejs["partials/layout/navigatePages"].toggleElement(serverInfoDiv); + } vmListInSubGroup(currentSubGroupId); } } else { @@ -533,19 +543,47 @@ export function handleCheck(type, id) { if (div.classList.contains("active")) { webconsolejs["partials/layout/navigatePages"].toggleElement(div); } + // Server Info도 닫기 + const serverInfoDiv = document.getElementById("subGroup_vm_info"); + if (serverInfoDiv && serverInfoDiv.classList.contains("active")) { + webconsolejs["partials/layout/navigatePages"].toggleElement(serverInfoDiv); + } } } else if (type === 'subgroup_vm') { if (checkbox.prop("checked")) { - // 기존 선택된 SubGroup VM이 있다면 해제 - if (selectedSubGroupVmId && selectedSubGroupVmId !== id) { - $(`#checkbox_subgroup_vm_${selectedSubGroupVmId}`).prop("checked", false); + // 같은 항목 재선택인지 확인 + if (selectedSubGroupVmId === id) { + // 같은 항목 재선택 - 토글 닫기 + selectedSubGroupVmId = null; + currentSubGroupVmId = null; + clearServerInfo(); + const div = document.getElementById("subGroup_vm_info"); + if (div && div.classList.contains("active")) { + webconsolejs["partials/layout/navigatePages"].toggleElement(div); + } + } else { + // 다른 항목 선택 - 기존 선택 해제 후 새 항목 선택 + if (selectedSubGroupVmId && selectedSubGroupVmId !== id) { + $(`#checkbox_subgroup_vm_${selectedSubGroupVmId}`).prop("checked", false); + } + selectedSubGroupVmId = id; + currentSubGroupVmId = id; + webconsolejs['pages/operation/manage/mci'].subGroup_vmDetailInfo(currentSubGroupVmId); + // Server Info 토글 (c 버튼 역할) + const div = document.getElementById("subGroup_vm_info"); + if (div && !div.classList.contains("active")) { + webconsolejs["partials/layout/navigatePages"].toggleElement(div); + } } - selectedSubGroupVmId = id; - currentSubGroupVmId = id; - webconsolejs['pages/operation/manage/mci'].subGroup_vmDetailInfo(currentSubGroupVmId); } else { selectedSubGroupVmId = null; + currentSubGroupVmId = null; clearServerInfo(); + // 체크 해제 시 토글 닫기 + const div = document.getElementById("subGroup_vm_info"); + if (div && div.classList.contains("active")) { + webconsolejs["partials/layout/navigatePages"].toggleElement(div); + } } } // 마지막 선택된 VM 강조 표시 @@ -873,13 +911,12 @@ export async function vmDetailInfo(vmId) { export async function subGroup_vmDetailInfo(vmId) { currentSubGroupVmId = vmId - // Toggle MCIS Info - var div = document.getElementById("subGroup_vm_info"); - - const hasActiveClass = div.classList.contains("active"); - if (!hasActiveClass) { - webconsolejs["partials/layout/navigatePages"].toggleElement(div) - } + // Server Info는 c 버튼으로만 제어되므로 자동 토글 제거 + // var div = document.getElementById("subGroup_vm_info"); + // const hasActiveClass = div.classList.contains("active"); + // if (!hasActiveClass) { + // webconsolejs["partials/layout/navigatePages"].toggleElement(div) + // } // get mci vm try { @@ -940,6 +977,7 @@ export async function subGroup_vmDetailInfo(vmId) { var vmDescription = data.description; var vmPublicIp = data.publicIP == undefined ? "" : data.publicIP; var vmSshKeyID = data.sshKeyId; + var cspVMID = data.uid; try { var imageId = data.imageId @@ -970,6 +1008,9 @@ export async function subGroup_vmDetailInfo(vmId) { $("#subgroup_mci_server_info_connection").empty() $("#subgroup_mci_server_info_connection").append(vmProviderIcon) + // CSP 정보 설정 + $("#subgroup_server_info_csp").text(providerName) + $("#subgroup_server_info_text").text(' [ ' + currentSubGroupId + ' / ' + vmName + ' ]') $("#subgroup_server_info_name").text(vmName + "/" + vmId) @@ -1098,6 +1139,21 @@ function clearServerInfo() { $("#server_info_public_ip").text("") $("#server_info_private_ip").text("") + // subgroup 필드들 초기화 + $("#subgroup_server_info_csp").text("") + $("#subgroup_server_info_region").text("") + $("#subgroup_server_info_zone").text("") + $("#subgroup_server_info_connection_name").text("") + $("#subgroup_server_info_cspVMID").text("") + $("#subgroup_server_info_vmspec_name").text("") + $("#subgroup_server_info_archi").text("") + $("#subgroup_server_info_public_ip").text("") + $("#subgroup_server_info_private_ip").text("") + $("#subgroup_server_info_os").text("") + $("#subgroup_server_info_start_time").text("") + $("#subgroup_server_info_public_dns").text("") + $("#subgroup_server_info_private_dns").text("") + // ip information $("#server_detail_info_public_ip_text").text("") $("#server_info_public_dns").val("") @@ -2306,13 +2362,13 @@ export async function deletePolicy() { await loadPolicyData(); // 현재 선택된 MCI가 있으면 해당 MCI를 다시 선택 (Policy 탭에서 이미 선택된 상태 유지) - if (currentMciId && mciListTable) { + if (window.currentMciId && mciListTable) { try { - const row = mciListTable.getRow(currentMciId); + const row = mciListTable.getRow(window.currentMciId); if (row) { // MCI 선택 상태 유지 var tempcurmciID = row.getCell("id").getValue(); - currentMciId = tempcurmciID; + window.currentMciId = tempcurmciID; // mci_info 요소가 이미 활성화되어 있는지 확인하고 필요시 활성화 const mciInfoElement = document.getElementById("mci_info"); @@ -2438,7 +2494,7 @@ export async function deployPolicy() { // API 호출 const response = await webconsolejs["common/api/services/mci_api"].createPolicy( window.currentNsId, - currentMciId, + window.currentMciId, requestData.policy ); @@ -2462,13 +2518,13 @@ export async function deployPolicy() { } // 현재 선택된 MCI가 있으면 해당 MCI를 다시 선택 - if (currentMciId && mciListTable) { + if (window.currentMciId && mciListTable) { try { - const row = mciListTable.getRow(currentMciId); + const row = mciListTable.getRow(window.currentMciId); if (row) { // 강제로 MCI 선택 상태로 만들기 var tempcurmciID = row.getCell("id").getValue(); - currentMciId = tempcurmciID; + window.currentMciId = tempcurmciID; // mci_info 요소를 직접 활성화 const mciInfoElement = document.getElementById("mci_info"); @@ -2597,9 +2653,9 @@ function buildPolicyRequestData(data) { command: data.command ? [data.command] : [], userName: data.userName }, - vmDynamicReq: { - commonImage: data.commonImage, - commonSpec: data.commonSpec, + subGroupDynamicReq: { + imageId: data.commonImage, + specId: data.commonSpec, connectionName: data.connectionName, description: data.description, label: { diff --git a/front/assets/js/pages/operation/manage/pmk.js b/front/assets/js/pages/operation/manage/pmk.js index 47e90331..0f000d81 100644 --- a/front/assets/js/pages/operation/manage/pmk.js +++ b/front/assets/js/pages/operation/manage/pmk.js @@ -1204,13 +1204,26 @@ export async function deployPmkDynamic() { } // 실제 클러스터 생성 데이터 준비 - const createData = { - imageId: commonImage, - specId: commonSpec, - connectionName: clusterData.connection, - name: clusterData.name, - nodeGroupName: isNodeGroupVisible ? $("#nodegroup_name_dynamic").val() : "" - }; + let createData; + + // Azure provider인 경우 테스트용 하드코딩된 값 사용 + if (clusterData.provider.toLowerCase() === 'azure') { + createData = { + imageId: "default", + specId: "azure+koreacentral+standard_b4ms", + name: clusterData.name, // 폼에서 입력한 값 사용 + nodeGroupName: isNodeGroupVisible ? $("#nodegroup_name_dynamic").val() : "k8sng01" // 폼에서 입력한 값이 있으면 사용, 없으면 기본값 + }; + } else { + // 다른 provider는 기존 로직 사용 + createData = { + imageId: commonImage, + specId: commonSpec, + connectionName: clusterData.connection, + name: clusterData.name, + nodeGroupName: isNodeGroupVisible ? $("#nodegroup_name_dynamic").val() : "" + }; + } // commonImage가 없으면 "default"로 설정 if (!createData.commonImage || createData.commonImage === "") { diff --git a/front/assets/js/pages/operation/workspace/csproles.js b/front/assets/js/pages/operation/workspace/csproles.js index 5e80b447..bcda5f37 100644 --- a/front/assets/js/pages/operation/workspace/csproles.js +++ b/front/assets/js/pages/operation/workspace/csproles.js @@ -2,7 +2,10 @@ import { TabulatorFull as Tabulator } from "tabulator-tables"; // 전역 변수 var cspRolesListTable; +var cspRolePoliciesTable; var checked_array = []; +var currentClickedCspRoleId = ""; +var selectedPolicyId = ""; // CSP Roles 목록 조회 export async function refreshCspRolesList() { @@ -75,8 +78,27 @@ function initCspRolesTable() { // 행 클릭 시 cspRolesListTable.on("rowClick", function (e, row) { - var tempcurRoleID = row.getCell("id").getValue(); - console.log("Selected CSP Role:", tempcurRoleID); + var tempCurrentCspRoleId = currentClickedCspRoleId; + currentClickedCspRoleId = row.getCell("id").getValue(); + + if (tempCurrentCspRoleId === currentClickedCspRoleId) { + // 같은 행 클릭 - 정보 영역 숨김 + webconsolejs["partials/layout/navigatePages"].deactiveElement(document.getElementById("csp_role_info")); + currentClickedCspRoleId = ""; + this.deselectRow(); + return; + } else { + // 다른 행 클릭 - 정보 영역 표시 + webconsolejs["partials/layout/navigatePages"].activeElement(document.getElementById("csp_role_info")); + this.deselectRow(); + this.selectRow(currentClickedCspRoleId); + + // 선택된 CSP Role 데이터 로드 + var selectedRole = row.getData(); + console.log("Selected CSP Role:", selectedRole); + getSelectedCspRoleData(selectedRole); + return; + } }); // 선택된 여러개 row에 대해 처리 @@ -173,6 +195,729 @@ function providerFormatterString(data) { return provider || ""; } +// 선택된 CSP Role 데이터 로드 +function getSelectedCspRoleData(roleData) { + // 제목 업데이트 + const titleElement = document.getElementById('csp_role_info_text'); + if (titleElement) { + titleElement.textContent = `(${roleData.name || 'Unknown Role'})`; + } + + // 상세 정보 업데이트 + updateElementText('csp_role_info_provider', roleData.provider || '-'); + updateElementText('csp_role_info_name', roleData.name || '-'); + updateElementText('csp_role_info_id', roleData.id || '-'); + updateElementText('csp_role_info_description', roleData.description || '-'); + + // 플랫폼 역할 정보 (현재는 Mock 데이터로 처리) + const platformRole = getPlatformRoleForCspRole(roleData); + updateElementText('csp_role_info_platform_role', platformRole); + + // 정책 정보 로드 + loadCspRolePolicies(roleData.id); +} + +// 요소 텍스트 업데이트 헬퍼 함수 +function updateElementText(elementId, text) { + const element = document.getElementById(elementId); + if (element) { + element.textContent = text; + } +} + +// CSP Role에 연결된 플랫폼 역할 조회 (Mock 데이터) +function getPlatformRoleForCspRole(roleData) { + // 실제로는 API를 통해 조회해야 하지만, 현재는 Mock 데이터로 처리 + const mockPlatformRoles = { + 'role-001': 'Admin', + 'role-002': 'User', + 'role-003': 'Viewer' + }; + + return mockPlatformRoles[roleData.id] || 'Not Assigned'; +} + +// CSP Role의 정책 정보 로드 +async function loadCspRolePolicies(roleId) { + try { + console.log("Loading policies for role:", roleId); + const policies = await window.webconsolejs["common/api/services/csproles_api"].getPoliciesByRoleId(roleId); + console.log("Loaded policies:", policies); + displayCspRolePolicies(policies); + } catch (error) { + console.error("정책 정보 로드 중 오류:", error); + displayCspRolePolicies([]); + } +} + +// 정책 정보 표시 (Tabulator 테이블 사용) +function displayCspRolePolicies(policies) { + const policiesContainer = document.getElementById('csp_role_policies_list'); + if (!policiesContainer) return; + + console.log("Displaying policies:", policies); + + // 기존 테이블이 있으면 제거 + if (cspRolePoliciesTable) { + cspRolePoliciesTable.destroy(); + } + + // 빈 데이터 처리 + if (!policies || policies.length === 0) { + policiesContainer.innerHTML = ` +
+ + + + + + +
이 CSP Role에 연결된 정책이 없습니다.
+
+ `; + return; + } + + // Tabulator 테이블 초기화 (CSP Roles 목록과 유사한 간단한 구조) + cspRolePoliciesTable = new Tabulator("#csp_role_policies_list", { + data: policies, + layout: "fitColumns", + pagination: "local", + paginationSize: 10, + paginationSizeSelector: [5, 10, 20, 50], + movableColumns: true, + resizableRows: true, + selectable: true, + columns: [ + { + formatter: "rowSelection", + titleFormatter: "rowSelection", + vertAlign: "middle", + hozAlign: "center", + headerHozAlign: "center", + headerSort: false, + width: 60, + }, + { + title: "Name", + field: "name", + vertAlign: "middle", + hozAlign: "center", + width: 300, + }, + { + title: "ID", + field: "id", + vertAlign: "middle", + hozAlign: "center", + width: 300, + }, + { + title: "Policy Type", + field: "provider", + formatter: providerFormatter, + vertAlign: "middle", + hozAlign: "center", + headerSort: false, + width: 120, + }, + { + title: "Description", + field: "description", + vertAlign: "middle", + hozAlign: "center", + maxWidth: 500, + }, + ] + }); + + // 정책 행 클릭 이벤트 추가 + cspRolePoliciesTable.on("rowClick", function (e, row) { + const policyData = row.getData(); + showPolicyDetailPanel(policyData); + }); + + console.log("Policies table initialized with", policies.length, "policies"); +} + +// Provider별 색상 반환 +function getProviderColor(provider) { + const colorMap = { + 'aws': 'warning', + 'azure': 'info', + 'gcp': 'primary', + 'alibaba': 'success', + 'tencent': 'secondary' + }; + return colorMap[provider] || 'secondary'; +} + +// 정책 추가 모달 표시 +function showAddPolicyModal() { + // 모달이 이미 열려있으면 초기화 + const modal = new bootstrap.Modal(document.getElementById('addPolicyModal')); + modal.show(); + + // Provider 목록 로드 + loadPolicyProviders(); + + // 검색 이벤트 리스너 추가 + document.getElementById('addPolicySearch').addEventListener('input', filterAvailablePolicies); + document.getElementById('addPolicyProvider').addEventListener('change', loadAvailablePolicies); +} + +// 정책 Provider 목록 로드 +async function loadPolicyProviders() { + try { + const providers = await window.webconsolejs["common/api/services/csproles_api"].getCspProviders(); + const select = document.getElementById('addPolicyProvider'); + select.innerHTML = ''; + + providers.forEach(provider => { + const option = document.createElement('option'); + option.value = provider; + option.textContent = provider.toUpperCase(); + select.appendChild(option); + }); + } catch (error) { + console.error("Provider 목록 로드 중 오류:", error); + } +} + +// 사용 가능한 정책 목록 로드 +async function loadAvailablePolicies() { + const provider = document.getElementById('addPolicyProvider').value; + const container = document.getElementById('availablePoliciesList'); + + if (!provider) { + container.innerHTML = '
Select a Policy Provider to view available policies
'; + return; + } + + try { + container.innerHTML = '
Loading policies...
'; + + const policies = await window.webconsolejs["common/api/services/csproles_api"].getCspPolicyList(provider); + + if (!policies || policies.length === 0) { + container.innerHTML = '
No policies available for this provider
'; + return; + } + + // 정책 목록 표시 + let html = '
'; + policies.forEach(policy => { + html += ` +
+
+
${policy.name}
+ ID: ${policy.id} +
+ ${policy.description || 'No description'} +
+
+ +
+
+ `; + }); + html += '
'; + + container.innerHTML = html; + } catch (error) { + console.error("정책 목록 로드 중 오류:", error); + container.innerHTML = '
Error loading policies
'; + } +} + +// 정책 검색 필터 +function filterAvailablePolicies() { + const searchTerm = document.getElementById('addPolicySearch').value.toLowerCase(); + const policyItems = document.querySelectorAll('.policy-item'); + + policyItems.forEach(item => { + const policyName = item.querySelector('h6').textContent.toLowerCase(); + const policyId = item.querySelector('small').textContent.toLowerCase(); + + if (policyName.includes(searchTerm) || policyId.includes(searchTerm)) { + item.style.display = 'block'; + } else { + item.style.display = 'none'; + } + }); +} + +// 선택된 정책 추가 +async function addSelectedPolicy() { + const selectedPolicy = document.querySelector('input[name="selectedPolicy"]:checked'); + + if (!selectedPolicy) { + alert('Please select a policy to add'); + return; + } + + if (!currentClickedCspRoleId) { + alert('Please select a CSP Role first'); + return; + } + + try { + const policyId = selectedPolicy.value; + const result = await window.webconsolejs["common/api/services/csproles_api"].bindPolicyToRole(currentClickedCspRoleId, policyId); + + if (result.success) { + alert('Policy added successfully'); + + // 모달 닫기 + const modal = bootstrap.Modal.getInstance(document.getElementById('addPolicyModal')); + modal.hide(); + + // 정책 목록 새로고침 + await loadCspRolePolicies(currentClickedCspRoleId); + } else { + throw new Error(result.message || 'Failed to add policy'); + } + } catch (error) { + console.error("정책 추가 중 오류:", error); + alert('Error adding policy: ' + error.message); + } +} + +// 정책 언바인딩 + +// 정책 목록 새로고침 +function refreshPoliciesList() { + if (currentClickedCspRoleId) { + loadCspRolePolicies(currentClickedCspRoleId); + } +} + +// 모든 정책 선택 (Tabulator 사용) +function selectAllPolicies() { + if (!cspRolePoliciesTable) { + console.warn("Policies table not initialized"); + return; + } + + cspRolePoliciesTable.selectRow(); + console.log("All policies selected"); +} + +// 모든 정책 선택 해제 (Tabulator 사용) +function unselectAllPolicies() { + if (!cspRolePoliciesTable) { + console.warn("Policies table not initialized"); + return; + } + + cspRolePoliciesTable.deselectRow(); + console.log("All policies deselected"); +} + +// 정책 언바인딩 +function unbindPolicies() { + if (!cspRolePoliciesTable) { + console.warn("Policies table not initialized"); + return; + } + + const selectedRows = cspRolePoliciesTable.getSelectedRows(); + if (selectedRows.length === 0) { + alert("선택된 정책이 없습니다."); + return; + } + + const selectedIds = selectedRows.map(row => row.getData().binding_id); + if (confirm(`선택된 ${selectedIds.length}개의 정책을 CSP Role에서 제거하시겠습니까?`)) { + try { + // TODO: 실제 일괄 언바인딩 API 호출 + console.log("정책 언바인딩:", selectedIds); + alert("선택된 정책들이 성공적으로 제거되었습니다."); + // 정책 목록 새로고침 + refreshPoliciesList(); + } catch (error) { + console.error("정책 언바인딩 중 오류:", error); + alert("정책 제거 중 오류가 발생했습니다."); + } + } +} + +// 정책 가져오기 (Import) +function importPolicy() { + // 파일 입력 요소 생성 + const fileInput = document.createElement('input'); + fileInput.type = 'file'; + fileInput.accept = '.json'; + fileInput.style.display = 'none'; + + fileInput.onchange = async function(event) { + const file = event.target.files[0]; + if (!file) return; + + try { + const fileContent = await readFileContent(file); + const policyData = JSON.parse(fileContent); + + // 정책 데이터 검증 + if (!policyData.name || !policyData.document) { + throw new Error('정책 파일에 필수 필드(name, document)가 없습니다.'); + } + + // 정책 생성 + const result = await window.webconsolejs["common/api/services/csproles_api"].createCspPolicy(policyData); + + if (result.success) { + alert('정책이 성공적으로 가져왔습니다.'); + // 정책 목록 새로고침 + refreshPoliciesList(); + } else { + throw new Error(result.message || 'Failed to import policy'); + } + } catch (error) { + console.error("정책 가져오기 중 오류:", error); + alert('정책 가져오기 중 오류가 발생했습니다: ' + error.message); + } + }; + + // 파일 선택 다이얼로그 표시 + document.body.appendChild(fileInput); + fileInput.click(); + document.body.removeChild(fileInput); +} + +// 파일 내용 읽기 헬퍼 함수 +function readFileContent(file) { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = (e) => resolve(e.target.result); + reader.onerror = (e) => reject(e); + reader.readAsText(file); + }); +} + +// 정책 상세 보기 +function viewPolicy(bindingId) { + console.log("Viewing policy details for binding:", bindingId); + showPolicyEditor(bindingId); +} + +// 정책 에디터 모달 표시 +async function showPolicyEditor(bindingId) { + try { + // 정책 상세 정보 로드 + const policy = await window.webconsolejs["common/api/services/csproles_api"].getCspPolicyById(bindingId); + + // 모달 필드 채우기 + document.getElementById('policyEditorName').value = policy.name || ''; + document.getElementById('policyEditorId').value = policy.id || ''; + document.getElementById('policyEditorType').value = policy.provider || ''; + document.getElementById('policyEditorDescription').value = policy.description || ''; + document.getElementById('policyEditorDocument').value = JSON.stringify(policy.document || {}, null, 2); + + // 미리보기 업데이트 + updatePolicyDocumentPreview(); + + // 모달 표시 + const modal = new bootstrap.Modal(document.getElementById('policyEditorModal')); + modal.show(); + + // 이벤트 리스너 추가 + addPolicyDocumentListeners(); + + } catch (error) { + console.error("정책 정보 로드 중 오류:", error); + alert('Error loading policy details: ' + error.message); + } +} + +// 정책 문서 토글 +function togglePolicyDocument() { + const container = document.getElementById('policyDocumentContainer'); + const preview = document.getElementById('policyDocumentPreview'); + const toggleText = document.getElementById('policyDocumentToggleText'); + + if (container.style.display === 'none') { + container.style.display = 'block'; + preview.style.display = 'none'; + toggleText.textContent = 'Collapse'; + } else { + container.style.display = 'none'; + preview.style.display = 'block'; + toggleText.textContent = 'Expand'; + } +} + +// 정책 문서 미리보기 업데이트 +function updatePolicyDocumentPreview() { + const document = document.getElementById('policyEditorDocument').value; + const preview = document.getElementById('policyDocumentPreview'); + + try { + const parsed = JSON.parse(document); + preview.innerHTML = `
${JSON.stringify(parsed, null, 2)}
`; + } catch (error) { + preview.innerHTML = '
Invalid JSON format
'; + } +} + +// 정책 문서 에디터 이벤트 리스너 추가 +function addPolicyDocumentListeners() { + const documentTextarea = document.getElementById('policyEditorDocument'); + if (documentTextarea) { + documentTextarea.addEventListener('input', updatePolicyDocumentPreview); + } +} + +// 정책 문서 클리어 +function clearPolicyDocument() { + if (confirm('정책 문서를 클리어하시겠습니까?')) { + document.getElementById('policyEditorDocument').value = ''; + updatePolicyDocumentPreview(); + } +} + +// 정책 문서 편집 모드 +function editPolicyDocument() { + const editBtn = document.querySelector('button[onclick="editPolicyDocument()"]'); + const saveBtn = document.querySelector('button[onclick="savePolicyDocument()"]'); + const documentTextarea = document.getElementById('policyEditorDocument'); + + editBtn.style.display = 'none'; + saveBtn.style.display = 'inline-block'; + documentTextarea.readOnly = false; + documentTextarea.focus(); +} + +// 정책 문서 저장 +async function savePolicyDocument() { + const policyId = document.getElementById('policyEditorId').value; + const document = document.getElementById('policyEditorDocument').value; + + try { + // JSON 유효성 검사 + JSON.parse(document); + + const result = await window.webconsolejs["common/api/services/csproles_api"].updateCspPolicy(policyId, { + document: JSON.parse(document) + }); + + if (result.success) { + alert('정책이 성공적으로 업데이트되었습니다.'); + + // 편집 모드 해제 + const editBtn = document.querySelector('button[onclick="editPolicyDocument()"]'); + const saveBtn = document.querySelector('button[onclick="savePolicyDocument()"]'); + const documentTextarea = document.getElementById('policyEditorDocument'); + + editBtn.style.display = 'inline-block'; + saveBtn.style.display = 'none'; + documentTextarea.readOnly = true; + + // 미리보기 업데이트 + updatePolicyDocumentPreview(); + } else { + throw new Error(result.message || 'Failed to update policy'); + } + } catch (error) { + if (error instanceof SyntaxError) { + alert('Invalid JSON format. Please check your syntax.'); + } else { + console.error("정책 업데이트 중 오류:", error); + alert('Error updating policy: ' + error.message); + } + } +} + +// 정책 동기화 +async function syncPolicies() { + if (!currentClickedCspRoleId) { + alert('Please select a CSP Role first'); + return; + } + + if (confirm('정책을 동기화하시겠습니까?')) { + try { + const result = await window.webconsolejs["common/api/services/csproles_api"].syncPolicies(currentClickedCspRoleId); + + if (result.success) { + alert('정책이 성공적으로 동기화되었습니다.'); + // 정책 목록 새로고침 + await loadCspRolePolicies(currentClickedCspRoleId); + } else { + throw new Error(result.message || 'Failed to sync policies'); + } + } catch (error) { + console.error("정책 동기화 중 오류:", error); + alert('Error syncing policies: ' + error.message); + } + } +} + +// ===== 정책 상세 패널 관리 ===== + +// 정책 상세 패널 표시 +function showPolicyDetailPanel(policyData) { + selectedPolicyId = policyData.id; + + // 헤더 업데이트 + document.getElementById('policy-detail-header').textContent = `${policyData.name} / (${policyData.id})`; + + // 기본 정보 업데이트 + document.getElementById('policy-detail-name').textContent = policyData.name || '-'; + document.getElementById('policy-detail-id').textContent = policyData.id || '-'; + document.getElementById('policy-detail-type').textContent = policyData.provider || '-'; + document.getElementById('policy-detail-description').textContent = policyData.description || '-'; + + // Context JSON 업데이트 + const contextJson = policyData.document || {}; + document.getElementById('policy-detail-context').value = JSON.stringify(contextJson, null, 2); + updatePolicyContextPreview(); + + // 패널 표시 + document.getElementById('policy-detail-panel').style.display = 'block'; + + // Context JSON 이벤트 리스너 추가 + const contextTextarea = document.getElementById('policy-detail-context'); + if (contextTextarea) { + contextTextarea.addEventListener('input', updatePolicyContextPreview); + } + + // 스크롤을 패널로 이동 + document.getElementById('policy-detail-panel').scrollIntoView({ behavior: 'smooth' }); +} + + +// 정책 Context JSON 토글 (모달로 열기) +function togglePolicyContextJson() { + const jsonContent = document.getElementById('policy-detail-context').value; + + // JSON 에디터 모달에 내용 설정 + document.getElementById('jsonEditorContent').value = jsonContent; + + // 모달 열기 + const modal = new bootstrap.Modal(document.getElementById('jsonEditorModal')); + modal.show(); +} + +// 정책 Context JSON 클리어 +function clearPolicyContextJson() { + if (confirm('정책 Context JSON을 클리어하시겠습니까?')) { + document.getElementById('policy-detail-context').value = ''; + updatePolicyContextPreview(); + } +} + +// JSON 에디터 모달 내부 함수들 +function clearJsonEditor() { + if (confirm('JSON 내용을 클리어하시겠습니까?')) { + document.getElementById('jsonEditorContent').value = ''; + } +} + +function saveJsonEditor() { + const jsonContent = document.getElementById('jsonEditorContent').value; + + try { + // JSON 유효성 검사 + JSON.parse(jsonContent); + + // 원래 위치에 내용 저장 + document.getElementById('policy-detail-context').value = jsonContent; + updatePolicyContextPreview(); + + // 모달 닫기 + const modal = bootstrap.Modal.getInstance(document.getElementById('jsonEditorModal')); + modal.hide(); + + alert('JSON이 성공적으로 저장되었습니다.'); + } catch (error) { + alert('유효하지 않은 JSON 형식입니다: ' + error.message); + } +} + +// 정책 Context 미리보기 업데이트 +function updatePolicyContextPreview() { + const context = document.getElementById('policy-detail-context').value; + const preview = document.getElementById('policy-context-preview'); + + try { + const parsed = JSON.parse(context); + preview.innerHTML = `
${JSON.stringify(parsed, null, 2)}
`; + } catch (error) { + preview.innerHTML = '
Invalid JSON format
'; + } +} + +// 선택된 정책 편집 +function editSelectedPolicy() { + if (!selectedPolicyId) { + alert('Please select a policy first'); + return; + } + + // 정책 에디터 모달로 이동 + showPolicyEditor(selectedPolicyId); +} + + +// 정책 문서 보기 +function viewPolicyDocument(policyId) { + console.log("Viewing policy document for policy:", policyId); + // TODO: 정책 문서 보기 모달 구현 + alert(`정책 문서 보기: ${policyId}\n\n이 기능은 향후 구현 예정입니다.`); +} + +// 정책 정렬 (Tabulator 내장 정렬 사용) +function sortPolicies(field, direction) { + console.log(`정책 정렬: ${field} ${direction}`); + + if (!cspRolePoliciesTable) { + console.warn("Policies table not initialized"); + return; + } + + // Tabulator의 내장 정렬 기능 사용 + cspRolePoliciesTable.setSort(field, direction === 'asc' ? 'asc' : 'desc'); + + console.log(`정책이 ${field} 기준으로 ${direction === 'asc' ? '오름차순' : '내림차순'} 정렬되었습니다.`); +} + +// 정책 내보내기 +function exportPolicies() { + // TODO: 정책 내보내기 기능 구현 + alert("정책 내보내기 기능은 향후 구현 예정입니다."); +} + +// 정책 필터 초기화 +function clearPoliciesFilter() { + document.getElementById('policies-filter-field').value = 'name'; + document.getElementById('policies-filter-type').value = 'like'; + document.getElementById('policies-filter-value').value = ''; + // TODO: 필터 적용 로직 구현 + refreshPoliciesList(); +} + +// 필터 클리어 버튼 이벤트 연결 +document.addEventListener('DOMContentLoaded', function() { + const clearButton = document.getElementById('policies-filter-clear'); + if (clearButton) { + clearButton.addEventListener('click', clearPoliciesFilter); + } +}); + +// 전역 함수로 노출 +window.showAddPolicyModal = showAddPolicyModal; +window.refreshPoliciesList = refreshPoliciesList; +window.selectAllPolicies = selectAllPolicies; +window.unselectAllPolicies = unselectAllPolicies; +window.unbindPolicies = unbindPolicies; +window.importPolicy = importPolicy; +window.viewPolicy = viewPolicy; +window.viewPolicyDocument = viewPolicyDocument; +window.sortPolicies = sortPolicies; +window.exportPolicies = exportPolicies; +window.clearPoliciesFilter = clearPoliciesFilter; + // CSP Role 삭제 (모달에서 호출) export async function deleteCspRole(roleId) { try { @@ -190,6 +935,10 @@ export async function deleteCspRole(roleId) { cspRolesListTable.deleteRow(roleId); } + // 정보 영역 숨기기 + webconsolejs["partials/layout/navigatePages"].deactiveElement(document.getElementById("csp_role_info")); + currentClickedCspRoleId = ""; + alert("CSP Role이 성공적으로 삭제되었습니다."); } else { throw new Error("CSP Role 삭제에 실패했습니다."); @@ -249,8 +998,178 @@ async function initCspRoles() { refreshCspRolesList(); // 데이터 로드 } +// CSP Role 추가 모달 표시 +function showAddCspRoleModal() { + const modal = new bootstrap.Modal(document.getElementById('addCspRoleModal')); + modal.show(); + + // Provider 목록 로드 + loadCspRoleProviders(); + + // 검색 이벤트 리스너 추가 + document.getElementById('addCspRoleSearch').addEventListener('input', filterAvailableCspRoles); + document.getElementById('addCspRoleProvider').addEventListener('change', loadAvailableCspRoles); +} + +// CSP Role Provider 목록 로드 +async function loadCspRoleProviders() { + try { + const providers = await window.webconsolejs["common/api/services/csproles_api"].getCspProviders(); + const select = document.getElementById('addCspRoleProvider'); + select.innerHTML = ''; + + providers.forEach(provider => { + const option = document.createElement('option'); + option.value = provider; + option.textContent = provider.toUpperCase(); + select.appendChild(option); + }); + } catch (error) { + console.error("Provider 목록 로드 중 오류:", error); + } +} + +// 사용 가능한 CSP Role 목록 로드 +async function loadAvailableCspRoles() { + const provider = document.getElementById('addCspRoleProvider').value; + const container = document.getElementById('availableCspRolesList'); + + if (!provider) { + container.innerHTML = '
Select a CSP Provider to view available roles
'; + return; + } + + try { + container.innerHTML = '
Loading roles...
'; + + const roles = await window.webconsolejs["common/api/services/csproles_api"].getCspRoleList(provider); + + if (!roles || roles.length === 0) { + container.innerHTML = '
No roles available for this provider
'; + return; + } + + // 역할 목록 표시 + let html = '
'; + roles.forEach(role => { + html += ` +
+
+
${role.name}
+ ID: ${role.id} +
+ ${role.description || 'No description'} +
+
+ +
+
+ `; + }); + html += '
'; + + container.innerHTML = html; + } catch (error) { + console.error("CSP Role 목록 로드 중 오류:", error); + container.innerHTML = '
Error loading roles
'; + } +} + +// CSP Role 검색 필터 +function filterAvailableCspRoles() { + const searchTerm = document.getElementById('addCspRoleSearch').value.toLowerCase(); + const roleItems = document.querySelectorAll('.csp-role-item'); + + roleItems.forEach(item => { + const roleName = item.querySelector('h6').textContent.toLowerCase(); + const roleId = item.querySelector('small').textContent.toLowerCase(); + + if (roleName.includes(searchTerm) || roleId.includes(searchTerm)) { + item.style.display = 'block'; + } else { + item.style.display = 'none'; + } + }); +} + +// 선택된 CSP Role 추가 +async function addSelectedCspRole() { + const selectedRole = document.querySelector('input[name="selectedCspRole"]:checked'); + + if (!selectedRole) { + alert('Please select a CSP Role to add'); + return; + } + + try { + const roleId = selectedRole.value; + const roleData = { + id: roleId, + name: selectedRole.closest('.csp-role-item').querySelector('h6').textContent, + description: selectedRole.closest('.csp-role-item').querySelectorAll('small')[1].textContent.replace('No description', ''), + provider: document.getElementById('addCspRoleProvider').value + }; + + const result = await window.webconsolejs["common/api/services/csproles_api"].createCspRole(roleData); + + if (result.success) { + alert('CSP Role added successfully'); + + // 모달 닫기 + const modal = bootstrap.Modal.getInstance(document.getElementById('addCspRoleModal')); + modal.hide(); + + // CSP Role 목록 새로고침 + await refreshCspRolesList(); + } else { + throw new Error(result.message || 'Failed to add CSP Role'); + } + } catch (error) { + console.error("CSP Role 추가 중 오류:", error); + alert('Error adding CSP Role: ' + error.message); + } +} + +// CSP Role 동기화 +async function syncCspRoles() { + if (confirm('CSP Role을 동기화하시겠습니까?')) { + try { + const result = await window.webconsolejs["common/api/services/csproles_api"].syncCspRoles(); + + if (result.success) { + alert('CSP Role이 성공적으로 동기화되었습니다.'); + // CSP Role 목록 새로고침 + await refreshCspRolesList(); + } else { + throw new Error(result.message || 'Failed to sync CSP Roles'); + } + } catch (error) { + console.error("CSP Role 동기화 중 오류:", error); + alert('Error syncing CSP Roles: ' + error.message); + } + } +} + // 전역 함수로 노출 (HTML에서 호출용) window.refreshCspRolesList = refreshCspRolesList; window.deleteCspRole = deleteCspRole; window.applyFilter = applyFilter; window.clearFilter = clearFilter; +window.showAddCspRoleModal = showAddCspRoleModal; +window.addSelectedCspRole = addSelectedCspRole; +window.syncCspRoles = syncCspRoles; +window.showAddPolicyModal = showAddPolicyModal; +window.addSelectedPolicy = addSelectedPolicy; +window.syncPolicies = syncPolicies; +window.togglePolicyDocument = togglePolicyDocument; +window.clearPolicyDocument = clearPolicyDocument; +window.editPolicyDocument = editPolicyDocument; +window.savePolicyDocument = savePolicyDocument; +window.addPolicyDocumentListeners = addPolicyDocumentListeners; +window.showPolicyDetailPanel = showPolicyDetailPanel; +window.togglePolicyContextJson = togglePolicyContextJson; +window.clearPolicyContextJson = clearPolicyContextJson; +window.updatePolicyContextPreview = updatePolicyContextPreview; +window.clearJsonEditor = clearJsonEditor; +window.saveJsonEditor = saveJsonEditor; +window.editSelectedPolicy = editSelectedPolicy; diff --git a/front/assets/js/pages/operation/workspace/roles.js b/front/assets/js/pages/operation/workspace/roles.js index 79b27419..d4c80914 100644 --- a/front/assets/js/pages/operation/workspace/roles.js +++ b/front/assets/js/pages/operation/workspace/roles.js @@ -1,6 +1,14 @@ import { TabulatorFull as Tabulator } from "tabulator-tables"; import 'jstree'; +// webconsolejs 네임스페이스 초기화 +if (typeof webconsolejs === 'undefined') { + window.webconsolejs = {}; +} +if (typeof webconsolejs['pages/operation/workspace/roles'] === 'undefined') { + webconsolejs['pages/operation/workspace/roles'] = {}; +} + // CSS 스타일 추가 const style = document.createElement('style'); style.textContent = ` @@ -3243,3 +3251,191 @@ function setupHeaderClickEvents() { } } +// Assign User 관련 함수들 +let assignUserModalTable = null; +let currentSelectedRole = null; + +// Assign User 모달 초기화 +function initAssignUserModal(roleData) { + currentSelectedRole = roleData; + + // 역할 정보 표시 + const roleDisplay = document.getElementById('assign-user-modal-role-display'); + if (roleDisplay) { + roleDisplay.value = roleData ? `${roleData.name} (${roleData.id})` : ''; + } + + // 사용자 목록 로드 및 테이블 초기화 + loadUsersForAssignment(); +} + +// 사용자 목록 로드 +async function loadUsersForAssignment() { + try { + // 사용자 목록 API 호출 (users.js와 동일한 API 사용) + const users = await webconsolejs['common/api/services/users_api'].getUserList(); + initUserTable(users); + } catch (error) { + console.error('사용자 목록 로드 실패:', error); + // 에러 시 빈 배열로 초기화 + initUserTable([]); + } +} + +// 사용자 테이블 초기화 +function initUserTable(users) { + const tableElement = document.getElementById('assign-user-modal-userselector'); + if (!tableElement) return; + + // 기존 테이블 제거 + if (assignUserModalTable) { + assignUserModalTable.destroy(); + } + + try { + // Tabulator 테이블 초기화 + assignUserModalTable = new Tabulator("#assign-user-modal-userselector", { + data: users, + layout: "fitColumns", + height: 280, + pagination: true, + paginationSize: 10, + paginationSizeSelector: [10, 20, 30], + reactiveData: true, + selectable: true, + selectableCheck: function(row) { + return true; // 모든 행 선택 가능 + }, + columns: [ + { + formatter: "rowSelection", + titleFormatter: "rowSelection", + vertAlign: "middle", + hozAlign: "center", + headerHozAlign: "center", + headerSort: false, + width: 60, + }, + { + title: "Name", + field: "name", + sorter: "string", + formatter: function(cell) { + const user = cell.getRow().getData(); + const firstName = user.firstName || user.first_name || user.FirstName || ''; + const lastName = user.lastName || user.last_name || user.LastName || ''; + const email = user.email || user.Email || ''; + return `${firstName} ${lastName}`.trim() || email; + } + }, + { + title: "Email", + field: "email", + sorter: "string", + formatter: function(cell) { + const user = cell.getRow().getData(); + return user.email || user.Email || ''; + } + }, + { + title: "Status", + field: "enabled", + sorter: "boolean", + formatter: function(cell) { + const user = cell.getRow().getData(); + const enabled = user.enabled !== undefined ? user.enabled : + user.Enabled !== undefined ? user.Enabled : + user.status === 'active' || user.Status === 'active'; + return enabled ? 'Enabled' : 'Disabled'; + } + } + ] + }); + + } catch (error) { + console.error('사용자 테이블 초기화 실패:', error); + } +} + +// 사용자 할당 실행 +function assignUser() { + if (!currentSelectedRole) { + console.error('선택된 역할이 없습니다.'); + return; + } + + if (!assignUserModalTable) { + console.error('사용자 테이블이 초기화되지 않았습니다.'); + return; + } + + const selectedRows = assignUserModalTable.getSelectedRows(); + if (!selectedRows || selectedRows.length === 0) { + alert('할당할 사용자를 선택해주세요.'); + return; + } + + // 선택된 사용자들의 ID 추출 + const selectedUserIds = selectedRows.map(row => { + const user = row.getData(); + return user.id || user.Id || user.username || user.Username; + }); + + // 사용자 할당 API 호출 + assignUsersToRole(currentSelectedRole.id, selectedUserIds); +} + +// 실제 사용자 할당 API 호출 +async function assignUsersToRole(roleId, userIds) { + try { + // 각 사용자에 대해 역할 할당 API 호출 + const promises = userIds.map(userId => + webconsolejs['common/api/services/roles_api'].assignUserToRole(roleId, userId) + ); + + await Promise.all(promises); + + // 성공 메시지 표시 + alert('사용자가 성공적으로 할당되었습니다.'); + + // 모달 닫기 + const modal = bootstrap.Modal.getInstance(document.getElementById('assign-user-modal')); + if (modal) modal.hide(); + + // 테이블 새로고침 + if (typeof refreshRolesList === 'function') { + refreshRolesList(); + } + + } catch (error) { + console.error('사용자 할당 실패:', error); + alert('사용자 할당 중 오류가 발생했습니다: ' + error.message); + } +} + +// 모달이 열릴 때 호출되는 함수 (드롭다운에서 호출) +window.openAssignUserModal = function() { + // 현재 선택된 역할 정보 가져오기 + const selectedRole = AppState.roles.selectedRole; + + if (!selectedRole) { + alert('먼저 역할을 선택해주세요.'); + return; + } + + initAssignUserModal(selectedRole); + const modal = new bootstrap.Modal(document.getElementById('assign-user-modal')); + modal.show(); +}; + +// webconsolejs 네임스페이스에 함수 등록 (DOM 로드 후) +document.addEventListener('DOMContentLoaded', function() { + webconsolejs['pages/operation/workspace/roles'].openAssignUserModal = window.openAssignUserModal; + webconsolejs['pages/operation/workspace/roles'].assignUser = assignUser; +}); + +// 즉시 실행도 추가 (DOM 로드 전에도 사용 가능하도록) +webconsolejs['pages/operation/workspace/roles'].openAssignUserModal = window.openAssignUserModal; +webconsolejs['pages/operation/workspace/roles'].assignUser = assignUser; + + diff --git a/front/assets/js/partials/operation/manage/pmk_imagerecommendation.js b/front/assets/js/partials/operation/manage/pmk_imagerecommendation.js index 13a582dc..83abe28b 100644 --- a/front/assets/js/partials/operation/manage/pmk_imagerecommendation.js +++ b/front/assets/js/partials/operation/manage/pmk_imagerecommendation.js @@ -207,7 +207,7 @@ export async function getRecommendImageInfoPmk() { // API 호출을 위한 파라미터 구성 var searchParams = { includeDeprecatedImage: false, - isGPUImage: isGPUImage === "false", + isGPUImage: isGPUImage === "true", isKubernetesImage: false, isRegisteredByAsset: false, osArchitecture: osArchitecture, diff --git a/front/assets/js/partials/operation/manage/pmk_serverrecommendation.js b/front/assets/js/partials/operation/manage/pmk_serverrecommendation.js index 10f9ba53..6b5e0c03 100644 --- a/front/assets/js/partials/operation/manage/pmk_serverrecommendation.js +++ b/front/assets/js/partials/operation/manage/pmk_serverrecommendation.js @@ -210,6 +210,20 @@ export async function getRecommendVmInfoPmk() { } + // Architecture 필터링 추가 + var architectureVal = $("#assist_architecture-pmk").val() + if (architectureVal != "") { + var filterPolicy = { + "condition": [ + { + "operand": architectureVal + } + ], + "metric": "architecture" + } + policyArr.push(filterPolicy) + } + // 우선순위 정책 설정 const priorityArr = []; if (lat && lon) { diff --git a/front/templates/pages/operations/manage/workloads/mciworkloads.html b/front/templates/pages/operations/manage/workloads/mciworkloads.html index a259807c..b731c0a9 100644 --- a/front/templates/pages/operations/manage/workloads/mciworkloads.html +++ b/front/templates/pages/operations/manage/workloads/mciworkloads.html @@ -658,7 +658,7 @@

Create MCI

id="mci_plusVmIcon" onclick="webconsolejs['partials/operation/manage/mcicreate'].displayNewServerForm()" > - + VM + + SubGroup @@ -1123,7 +1123,7 @@

Server Configuration

- +
diff --git a/front/templates/pages/operations/manage/workloads/pmkworkloads.html b/front/templates/pages/operations/manage/workloads/pmkworkloads.html index e9817981..c0b959e9 100644 --- a/front/templates/pages/operations/manage/workloads/pmkworkloads.html +++ b/front/templates/pages/operations/manage/workloads/pmkworkloads.html @@ -1124,7 +1124,7 @@
@@ -1135,7 +1135,7 @@
diff --git a/front/templates/pages/operations/manage/workspaces/csproles.html b/front/templates/pages/operations/manage/workspaces/csproles.html index aeaf506b..9d6504b9 100644 --- a/front/templates/pages/operations/manage/workspaces/csproles.html +++ b/front/templates/pages/operations/manage/workspaces/csproles.html @@ -74,6 +74,23 @@

List of CSP Roles

+ +
+
+

+ CSP Role Info +

+
+
+
+
+ +
+ +
+
+ +
+
+
+
+
CSP
+
-
+
+
+
Name
+
-
+
+
+
ID
+
-
+
+
+
Description
+
-
+
+
+
Platform Role
+
-
+
+
+
+
+ +
+
+
+
+ + + +
+
+
+
+ Field +
+ +
+ +
+
+ Type +
+ +
+ +
+
+ Filter +
+ + +
+
+
+ +
+
+ +
+ + + +
+
+
+
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + <%= partial("partials/layout/pageloader.html") %> diff --git a/front/templates/pages/operations/manage/workspaces/roles.html b/front/templates/pages/operations/manage/workspaces/roles.html index 2ec4a383..8970eb37 100644 --- a/front/templates/pages/operations/manage/workspaces/roles.html +++ b/front/templates/pages/operations/manage/workspaces/roles.html @@ -82,6 +82,16 @@

List of Roles

Delete + + + + + + + + + Assign User + @@ -640,9 +650,77 @@

CSP Role

+ + + <%= partial("partials/layout/pageloader.html") %> <%= javascriptTag("common/api/services/roles_api.js") %> <%= javascriptTag("pages/operation/workspace/roles.js") %> +<%= javascriptTag("common/api/services/users_api.js") %> diff --git a/front/templates/partials/operation/manage/_pmk_serverrecommendation.html b/front/templates/partials/operation/manage/_pmk_serverrecommendation.html index 4fc755a3..2c7378a7 100644 --- a/front/templates/partials/operation/manage/_pmk_serverrecommendation.html +++ b/front/templates/partials/operation/manage/_pmk_serverrecommendation.html @@ -149,6 +149,22 @@

+ +
+
+ + +
+
diff --git a/front/templates/partials/operation/manage/_subgroupvminfo.html b/front/templates/partials/operation/manage/_subgroupvminfo.html index c75da49a..2c89a05e 100644 --- a/front/templates/partials/operation/manage/_subgroupvminfo.html +++ b/front/templates/partials/operation/manage/_subgroupvminfo.html @@ -9,14 +9,6 @@

Terminal --> - - Terminal -
- +
-
Operating System
-
-
-
-
Architecture
-
-
-
-
StartTime
-
-
-
- - -
-
-
Private IP
-
-
-
-
Public DNS
-
-
- -
-
-
+
CSP
+
- -