Control 보안 정책 설정 확장
This commit is contained in:
+72
-12
@@ -616,6 +616,54 @@
|
||||
if (!byGroup[group]) byGroup[group] = [];
|
||||
byGroup[group].push(item);
|
||||
});
|
||||
const fieldHtml = item => {
|
||||
const key = escapeHtml(item.key);
|
||||
const value = item.value ?? item.default ?? '';
|
||||
const type = item.type || 'number';
|
||||
if (type === 'boolean') {
|
||||
const checked = value === true || value === 1 || value === '1' || value === 'true';
|
||||
return `
|
||||
<span class="settings-toggle">
|
||||
<input data-setting-key="${key}" type="checkbox" value="1" ${checked ? 'checked' : ''}>
|
||||
<span>${checked ? 'ON' : 'OFF'}</span>
|
||||
</span>
|
||||
`;
|
||||
}
|
||||
if (type === 'select') {
|
||||
const options = item.options || {};
|
||||
return `
|
||||
<select data-setting-key="${key}" class="settings-select">
|
||||
${Object.entries(options).map(([optionValue, label]) => `
|
||||
<option value="${escapeHtml(optionValue)}" ${String(optionValue) === String(value) ? 'selected' : ''}>${escapeHtml(label)}</option>
|
||||
`).join('')}
|
||||
</select>
|
||||
`;
|
||||
}
|
||||
if (type === 'text') {
|
||||
return `
|
||||
<input
|
||||
data-setting-key="${key}"
|
||||
type="text"
|
||||
maxlength="${escapeHtml(item.max_length ?? 255)}"
|
||||
value="${escapeHtml(value)}">
|
||||
`;
|
||||
}
|
||||
return `
|
||||
<input
|
||||
data-setting-key="${key}"
|
||||
type="number"
|
||||
min="${escapeHtml(item.min ?? '')}"
|
||||
max="${escapeHtml(item.max ?? '')}"
|
||||
step="${escapeHtml(item.step ?? '1')}"
|
||||
value="${escapeHtml(value)}">
|
||||
`;
|
||||
};
|
||||
const defaultValueText = item => {
|
||||
if (item.type === 'select' && item.options && item.options[item.default]) {
|
||||
return item.options[item.default];
|
||||
}
|
||||
return item.default ?? '';
|
||||
};
|
||||
|
||||
els.settingsBody.innerHTML = Object.entries(byGroup).map(([group, items]) => `
|
||||
<section class="settings-group">
|
||||
@@ -628,20 +676,29 @@
|
||||
<span>${escapeHtml(item.unit || '')}</span>
|
||||
</span>
|
||||
<span class="settings-control">
|
||||
<input
|
||||
data-setting-key="${escapeHtml(item.key)}"
|
||||
type="number"
|
||||
min="${escapeHtml(item.min ?? '')}"
|
||||
max="${escapeHtml(item.max ?? '')}"
|
||||
step="${escapeHtml(item.step ?? '1')}"
|
||||
value="${escapeHtml(item.value ?? item.default ?? '')}">
|
||||
<span class="settings-default">${t('settingsDefault')} ${escapeHtml(item.default ?? '')}</span>
|
||||
${fieldHtml(item)}
|
||||
<span class="settings-default">${t('settingsDefault')} ${escapeHtml(defaultValueText(item))}</span>
|
||||
</span>
|
||||
</label>
|
||||
`).join('')}
|
||||
</div>
|
||||
</section>
|
||||
`).join('');
|
||||
els.settingsBody.querySelectorAll('.settings-toggle input').forEach(input => {
|
||||
input.addEventListener('change', () => {
|
||||
const label = input.closest('.settings-toggle')?.querySelector('span');
|
||||
if (label) label.textContent = input.checked ? 'ON' : 'OFF';
|
||||
});
|
||||
input.closest('.settings-toggle')?.addEventListener('click', () => {
|
||||
input.checked = !input.checked;
|
||||
input.dispatchEvent(new Event('change'));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function settingByKey(key, fallback = null) {
|
||||
const item = (state.settingsPayload?.items || []).find(row => row.key === key);
|
||||
return item ? item.value : fallback;
|
||||
}
|
||||
|
||||
async function openSettings() {
|
||||
@@ -668,7 +725,9 @@
|
||||
function currentSettingsFormValues() {
|
||||
const values = {};
|
||||
els.settingsBody?.querySelectorAll('[data-setting-key]').forEach(input => {
|
||||
values[input.dataset.settingKey] = input.value;
|
||||
values[input.dataset.settingKey] = input.type === 'checkbox'
|
||||
? (input.checked ? '1' : '0')
|
||||
: input.value;
|
||||
});
|
||||
return values;
|
||||
}
|
||||
@@ -1906,12 +1965,13 @@
|
||||
title: t('rebootTitle'),
|
||||
okText: t('next'),
|
||||
danger: true,
|
||||
placeholder: t('rebootPhrase'),
|
||||
placeholder: String(settingByKey('security.reboot_phrase', t('rebootPhrase'))),
|
||||
autocomplete: 'off',
|
||||
});
|
||||
|
||||
if (phrase === null) return;
|
||||
if (String(phrase).trim() !== t('rebootPhrase')) {
|
||||
const expectedPhrase = String(settingByKey('security.reboot_phrase', t('rebootPhrase')));
|
||||
if (String(phrase).trim() !== expectedPhrase) {
|
||||
await window.customAlert(t('rebootPhraseMismatch'), {
|
||||
title: t('rebootCanceled'),
|
||||
danger: true,
|
||||
@@ -1932,7 +1992,7 @@
|
||||
els.rebootBtn.disabled = true;
|
||||
notice(t('rebootSending'), 'info');
|
||||
await api('reboot', {
|
||||
phrase: '재부팅',
|
||||
phrase: expectedPhrase,
|
||||
password,
|
||||
});
|
||||
await window.customAlert(t('rebootStartedBody'), {
|
||||
|
||||
Reference in New Issue
Block a user