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 = '';
+
+ 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 = '';
+
+ 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
+
+
+
+
+
+
+
+
+
+
+
+
<%= 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
+
-->
-
- Terminal
-
-
+
-
-
-
-
-
+
+
-
-
+
+
-
Config Name
-
+
Architecture
+
-
-