Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 20 additions & 106 deletions internal/server/static/policy-editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ let appState = {
availableRoles: [],
templates: [],
isDirty: false,
originalRules: null, // Store original rules to detect changes
originalRBAC: null, // Store original RBAC to detect changes
filters: {
ruleSearch: '',
category: ''
Expand Down Expand Up @@ -1234,42 +1232,6 @@ async function handleApplyTemplate(e) {
}

// ==================== Save & Load ====================
// Helper function to create stable JSON string for comparison
// Recursively sorts object keys to ensure consistent comparison
function createStableJSON(obj) {
if (obj === null || obj === undefined) {
return JSON.stringify(obj);
}

if (typeof obj !== 'object') {
return JSON.stringify(obj);
}

if (Array.isArray(obj)) {
// Recursively process array elements
const processedArray = obj.map(item => {
if (item && typeof item === 'object') {
return JSON.parse(createStableJSON(item));
}
return item;
});
return JSON.stringify(processedArray);
}

// Recursively process object properties
const sorted = {};
Object.keys(obj).sort().forEach(key => {
const value = obj[key];
if (value && typeof value === 'object') {
sorted[key] = JSON.parse(createStableJSON(value));
} else {
sorted[key] = value;
}
});

return JSON.stringify(sorted);
}

async function savePolicy() {
if (appState.settings.confirmSave) {
if (!confirm('정책을 저장하시겠습니까?')) return;
Expand Down Expand Up @@ -1326,70 +1288,27 @@ async function savePolicy() {
document.getElementById('floating-save-btn').classList.remove('ring-4', 'ring-yellow-400', 'animate-pulse');
showToast('정책이 성공적으로 저장되었습니다!');

console.log('[DEBUG] Policy saved successfully, checking for changes...');

// Check if rules or RBAC were changed and ask about conversion
const currentRules = createStableJSON(appState.policy.rules || []);
const currentRBAC = createStableJSON(appState.policy.rbac || {});
const rulesChanged = appState.originalRules && (appState.originalRules !== currentRules);
const rbacChanged = appState.originalRBAC && (appState.originalRBAC !== currentRBAC);

// Check if code-policy.json exists in the policy directory
const policyPath = appState.settings.policyPath || '.sym/user-policy.json';
const policyDir = policyPath.substring(0, policyPath.lastIndexOf('/'));
const codePolicyPath = policyDir + '/code-policy.json';
let codePolicyExists = false;

try {
const response = await fetch(codePolicyPath);
codePolicyExists = response.ok;
} catch (error) {
codePolicyExists = false;
}

console.log('=== Policy Change Detection ===');
console.log('Original rules:', appState.originalRules ? appState.originalRules.substring(0, 100) + '...' : 'NULL');
console.log('Current rules:', currentRules.substring(0, 100) + '...');
console.log('Original RBAC:', appState.originalRBAC ? appState.originalRBAC.substring(0, 100) + '...' : 'NULL');
console.log('Current RBAC:', currentRBAC.substring(0, 100) + '...');
console.log('Rules changed?', rulesChanged);
console.log('RBAC changed?', rbacChanged);
console.log('code-policy.json exists?', codePolicyExists);
console.log('============================');

if (rulesChanged || rbacChanged || !codePolicyExists) {
const changedItems = [];
if (rulesChanged) changedItems.push('규칙');
if (rbacChanged) changedItems.push('RBAC 설정');
if (!codePolicyExists) changedItems.push('code-policy.json 파일이 없음');

const message = changedItems.length > 0 ?
`${changedItems.join(', ')}.\n\n` +
'linter 설정 파일(ESLint, Checkstyle, PMD 등)을 자동으로 생성하시겠습니까?\n\n' +
'이 작업은 설정된 LLM Provider를 사용하며 몇 분 정도 소요될 수 있습니다.' :
'linter 설정 파일(ESLint, Checkstyle, PMD 등)을 자동으로 생성하시겠습니까?\n\n' +
'이 작업은 설정된 LLM Provider를 사용하며 몇 분 정도 소요될 수 있습니다.';

const shouldConvert = confirm(message);

if (shouldConvert) {
try {
showToast('정책 변환 중... 잠시만 기다려주세요', 'info');
const convertResult = await API.convertPolicy();
showToast(
`변환 완료! ${convertResult.filesWritten.length}개의 파일이 생성되었습니다.`,
'success'
);
console.log('Convert result:', convertResult);
} catch (error) {
console.error('Conversion failed:', error);
showToast('변환 실패: ' + error.message, 'error');
}
// Always ask user about conversion after save
const message =
'저장이 완료되었습니다.\n\n' +
'linter 설정 파일을 생성/갱신하시겠습니까?\n' +
'(code-policy.json, ESLint, Checkstyle, PMD 등)\n\n' +
'• 확인: LLM을 사용하여 설정 파일 생성\n' +
'• 취소: 현재 설정 유지';

if (confirm(message)) {
try {
showToast('정책 변환 중... 잠시만 기다려주세요', 'info');
const convertResult = await API.convertPolicy();
showToast(
`변환 완료! ${convertResult.filesWritten.length}개의 파일이 생성되었습니다.`,
'success'
);
console.log('Convert result:', convertResult);
} catch (error) {
console.error('Conversion failed:', error);
showToast('변환 실패: ' + error.message, 'error');
}

// Update original rules and RBAC to current state to avoid re-asking
appState.originalRules = currentRules;
appState.originalRBAC = currentRBAC;
}
} catch (error) {
console.error('Failed to save policy:', error);
Expand All @@ -1400,12 +1319,7 @@ async function savePolicy() {
async function loadPolicy() {
try {
appState.policy = await API.getPolicy();
// Store original rules and RBAC for change detection (using stable JSON)
appState.originalRules = createStableJSON(appState.policy.rules || []);
appState.originalRBAC = createStableJSON(appState.policy.rbac || {});
console.log('Policy loaded. Rules count:', appState.policy.rules.length);
console.log('Original rules stored:', appState.originalRules.substring(0, 100) + '...');
console.log('Original RBAC stored:', appState.originalRBAC.substring(0, 100) + '...');
renderAll();
showToast('정책을 불러왔습니다');
} catch (error) {
Expand Down