Improve installer automation and config editor

This commit is contained in:
2026-05-08 19:29:07 +09:00
parent 5ff4e20b5e
commit 427b708277
12 changed files with 751 additions and 216 deletions

View File

@@ -1,6 +1,7 @@
const state = {
manifestUrl: '',
selectedPack: null,
selectedPackMeta: null,
installPath: '',
jdkPath: ''
}
@@ -9,6 +10,15 @@ const panelMap = new Map([...document.querySelectorAll('.panel')].map((panel) =>
const stepMap = new Map([...document.querySelectorAll('.steps li')].map((step) => [step.dataset.step, step]))
const logView = document.getElementById('logView')
const packList = document.getElementById('packList')
const manifestUrlInput = document.getElementById('manifestUrl')
const installPathInput = document.getElementById('installPath')
const jdkPathInput = document.getElementById('jdkPath')
const jdkStatus = document.getElementById('jdkStatus')
const jdkRecommended = document.getElementById('jdkRecommended')
const eulaBlock = document.getElementById('eulaBlock')
const eulaText = document.getElementById('eulaText')
const eulaLink = document.getElementById('eulaLink')
const startInstallButton = document.getElementById('startInstallButton')
function setActiveStep(step) {
for (const [key, panel] of panelMap.entries()) {
@@ -22,7 +32,9 @@ function setActiveStep(step) {
function appendLog(entry) {
if (entry?.action === 'eula-required') {
document.getElementById('eulaBlock').classList.remove('hidden')
eulaText.textContent = entry.eulaText ?? ''
eulaLink.href = entry.eulaUrl ?? '#'
eulaBlock.classList.remove('hidden')
return
}
@@ -54,10 +66,87 @@ function renderPackList(packs) {
})
}
async function loadPackManifest() {
state.manifestUrl = manifestUrlInput.value.trim()
state.selectedPack = null
state.selectedPackMeta = null
packList.innerHTML = '<div class="infoBox">서버팩 목록을 불러오는 중입니다.</div>'
try {
const manifest = await window.installerApi.loadPacks(state.manifestUrl)
renderPackList(manifest.packs)
} catch (error) {
packList.innerHTML = `<div class="infoBox">${error.message}</div>`
}
}
async function loadSelectedPackMeta() {
if (state.selectedPack == null) {
state.selectedPackMeta = null
return null
}
const packMeta = await window.installerApi.inspectPack(state.manifestUrl, state.selectedPack.file)
state.selectedPackMeta = packMeta
return packMeta
}
async function autoDetectJdkForSelectedPack() {
const packMeta = await loadSelectedPackMeta()
if (packMeta == null) {
jdkRecommended.textContent = '먼저 서버팩을 선택하세요.'
jdkStatus.textContent = ''
return
}
const recommendedVersion = packMeta.packDefinition.recommendedJdkVersion ?? null
jdkRecommended.textContent = recommendedVersion != null
? `선택한 서버팩의 권장 JDK 버전: ${recommendedVersion}`
: '선택한 서버팩에 권장 JDK 버전 정보가 없습니다.'
jdkStatus.textContent = 'JDK 자동 탐색 중입니다.'
const result = await window.installerApi.detectJdk(recommendedVersion)
if (result.detected != null) {
state.jdkPath = result.detected
jdkPathInput.value = result.detected
}
if (result.detected == null) {
jdkStatus.textContent = '설치 가능한 JDK를 찾지 못했습니다.'
return
}
const pickedCandidate = result.candidates.find((candidate) => candidate.path === result.detected)
const versionLabel = pickedCandidate?.majorVersion != null ? `JDK ${pickedCandidate.majorVersion}` : '버전 미확인 JDK'
if (result.recommendedVersion != null && result.exactMatch) {
jdkStatus.textContent = `권장 버전과 일치하는 ${versionLabel}를 자동 선택했습니다: ${result.detected}`
return
}
if (result.recommendedVersion != null) {
jdkStatus.textContent = `권장 JDK ${result.recommendedVersion}은 찾지 못해 ${versionLabel}를 대신 선택했습니다: ${result.detected}`
return
}
jdkStatus.textContent = `자동 탐색 성공: ${versionLabel} / ${result.detected}`
}
async function goToStep(step) {
setActiveStep(step)
if (step === 3) {
try {
await autoDetectJdkForSelectedPack()
} catch (error) {
jdkStatus.textContent = error.message
}
}
}
async function bootstrap() {
const defaults = await window.installerApi.getDefaults()
state.manifestUrl = defaults.manifestUrl
document.getElementById('manifestUrl').value = defaults.manifestUrl
manifestUrlInput.value = defaults.manifestUrl
await loadPackManifest()
}
window.installerApi.onLog(appendLog)
@@ -66,106 +155,106 @@ packList.addEventListener('change', () => {
const checked = packList.querySelector('input[name="packChoice"]:checked')
if (checked == null) {
state.selectedPack = null
state.selectedPackMeta = null
return
}
state.selectedPack = {
file: checked.value,
name: checked.dataset.packName ?? checked.value
}
state.selectedPackMeta = null
})
document.querySelectorAll('[data-back]').forEach((button) => {
button.addEventListener('click', () => {
setActiveStep(button.dataset.back)
button.addEventListener('click', async () => {
await goToStep(button.dataset.back)
})
})
document.getElementById('loadPacksButton').addEventListener('click', async () => {
state.manifestUrl = document.getElementById('manifestUrl').value.trim()
const manifest = await window.installerApi.loadPacks(state.manifestUrl)
renderPackList(manifest.packs)
manifestUrlInput.addEventListener('change', async () => {
await loadPackManifest()
})
document.getElementById('toStep2').addEventListener('click', () => {
document.getElementById('toStep2').addEventListener('click', async () => {
if (state.selectedPack == null) {
alert('서버팩을 먼저 선택하세요.')
return
}
setActiveStep(2)
await goToStep(2)
})
document.getElementById('browseInstallPath').addEventListener('click', async () => {
const selected = await window.installerApi.chooseDirectory()
if (selected != null) {
state.installPath = selected
document.getElementById('installPath').value = selected
installPathInput.value = selected
validateInstallPath(selected)
}
})
document.getElementById('installPath').addEventListener('input', (event) => {
installPathInput.addEventListener('input', (event) => {
state.installPath = event.target.value
validateInstallPath(state.installPath)
})
document.getElementById('toStep3').addEventListener('click', () => {
document.getElementById('toStep3').addEventListener('click', async () => {
if (!validateInstallPath(state.installPath)) {
alert('올바른 설치 경로를 입력하세요.')
return
}
setActiveStep(3)
})
document.getElementById('detectJdkButton').addEventListener('click', async () => {
const result = await window.installerApi.detectJdk()
document.getElementById('jdkStatus').textContent = result.detected != null
? `자동 탐색 성공: ${result.detected}`
: `JDK를 찾지 못했습니다. 탐색 경로: ${result.candidates.join(', ') || '없음'}`
if (result.detected != null) {
state.jdkPath = result.detected
document.getElementById('jdkPath').value = result.detected
}
await goToStep(3)
})
document.getElementById('browseJdkPath').addEventListener('click', async () => {
const selected = await window.installerApi.chooseJdk()
if (selected != null) {
state.jdkPath = selected
document.getElementById('jdkPath').value = selected
jdkPathInput.value = selected
}
})
document.getElementById('jdkPath').addEventListener('input', (event) => {
jdkPathInput.addEventListener('input', (event) => {
state.jdkPath = event.target.value
})
document.getElementById('toStep4').addEventListener('click', () => {
document.getElementById('toStep4').addEventListener('click', async () => {
if (state.jdkPath.trim().length === 0) {
alert('JDK 경로를 지정하세요.')
return
}
setActiveStep(4)
await goToStep(4)
})
document.getElementById('startInstallButton').addEventListener('click', async () => {
logView.textContent = ''
document.getElementById('eulaBlock').classList.add('hidden')
const result = await window.installerApi.startInstall({
manifestUrl: state.manifestUrl,
packFile: state.selectedPack.file,
installPath: state.installPath,
jdkPath: state.jdkPath
})
if (result.warning != null) {
appendLog({ message: result.warning, tone: 'warn' })
startInstallButton.addEventListener('click', async () => {
if (state.selectedPack == null) {
alert('서버팩을 먼저 선택하세요.')
return
}
logView.textContent = ''
eulaBlock.classList.add('hidden')
eulaText.textContent = ''
startInstallButton.disabled = true
try {
const result = await window.installerApi.startInstall({
manifestUrl: state.manifestUrl,
packFile: state.selectedPack.file,
installPath: state.installPath,
jdkPath: state.jdkPath
})
if (result.warning != null) {
appendLog({ message: result.warning, tone: 'warn' })
}
await goToStep(result.nextStep)
} finally {
startInstallButton.disabled = false
}
setActiveStep(result.nextStep)
})
document.getElementById('acceptEulaButton').addEventListener('click', async () => {
document.getElementById('eulaBlock').classList.add('hidden')
await window.installerApi.acceptEula()
eulaBlock.classList.add('hidden')
})
document.getElementById('openConfigEditorButton').addEventListener('click', async () => {
@@ -173,8 +262,8 @@ document.getElementById('openConfigEditorButton').addEventListener('click', asyn
document.getElementById('configEditorStatus').textContent = `브라우저에서 열림: ${url}`
})
document.getElementById('toStep6').addEventListener('click', () => {
setActiveStep(6)
document.getElementById('toStep6').addEventListener('click', async () => {
await goToStep(6)
})
document.getElementById('configurePortButton').addEventListener('click', async () => {
@@ -182,8 +271,8 @@ document.getElementById('configurePortButton').addEventListener('click', async (
document.getElementById('portStatusBox').textContent = result.message
})
document.getElementById('toStep7').addEventListener('click', () => {
setActiveStep(7)
document.getElementById('toStep7').addEventListener('click', async () => {
await goToStep(7)
})
document.getElementById('openFolderButton').addEventListener('click', async () => {