Build music-quiz installer and management site per spec
Implements the full spec described in README.md: Management site (Node + TypeScript + Express + EJS): - Public main page lists packs registered in manifest.json. - /op login (account.json, internal-only), /op/dashboard manages packs with horizontal-scroll cards, add/select-and-delete flow, and the /op/dashboard/:packName editor (Mojang release dropdown, dynamic mods/resourcepacks lists, platform/RAM fields, file rename). - Routes for /manifest.json (public) and /file/* (server pack files). - Middleware blocks /account.json and /manifest/* directory access. Installer (Electron): - Five page renderer driven by IPC (preload contextBridge API): pack pick → single/multi → server install (path no-Korean check, JDK detect, file download, EULA, RAM gating, local web config editor, UPnP/port-forward check) → client install (.mc_custom mods + resourcepacks + launcher_profiles.json gameDir/javaArgs) → finish toggles (server folder, shortcut, server start, launcher start). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
59
src/installer/preload.ts
Normal file
59
src/installer/preload.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { contextBridge, ipcRenderer } from 'electron'
|
||||
import type { ClientInstallPayload, FetchedPack, RamCheckResult, ServerInstallPayload, PortForwardResult } from './types'
|
||||
|
||||
const api = {
|
||||
// 1단계
|
||||
loadPacks: (manifestUrl?: string): Promise<FetchedPack[]> =>
|
||||
ipcRenderer.invoke('packs:load', manifestUrl),
|
||||
setSelectedPack: (packKey: string): Promise<void> =>
|
||||
ipcRenderer.invoke('packs:select', packKey),
|
||||
|
||||
// 3-1
|
||||
pickFolder: (): Promise<string | null> => ipcRenderer.invoke('dialog:pickFolder'),
|
||||
validateInstallPath: (target: string): Promise<{ ok: boolean; message?: string }> =>
|
||||
ipcRenderer.invoke('install:validatePath', target),
|
||||
|
||||
// 3-2
|
||||
detectJdk: (): Promise<{ found: boolean; path: string }> => ipcRenderer.invoke('jdk:detect'),
|
||||
|
||||
// 3-3
|
||||
startServerInstall: (payload: ServerInstallPayload): Promise<void> =>
|
||||
ipcRenderer.invoke('server:install', payload),
|
||||
acceptEula: (installPath: string): Promise<void> =>
|
||||
ipcRenderer.invoke('server:acceptEula', installPath),
|
||||
checkRam: (packKey: string): Promise<RamCheckResult> =>
|
||||
ipcRenderer.invoke('server:checkRam', packKey),
|
||||
|
||||
// 3-4
|
||||
startServerConfigEditor: (installPath: string): Promise<{ url: string }> =>
|
||||
ipcRenderer.invoke('server:configEditor', installPath),
|
||||
|
||||
// 3-5
|
||||
checkPortForward: (port: number): Promise<PortForwardResult> =>
|
||||
ipcRenderer.invoke('server:portForward', port),
|
||||
|
||||
// 4단계
|
||||
installClient: (payload: ClientInstallPayload): Promise<void> =>
|
||||
ipcRenderer.invoke('client:install', payload),
|
||||
|
||||
// 5단계
|
||||
openServerFolder: (): Promise<void> => ipcRenderer.invoke('finish:openServerFolder'),
|
||||
createDesktopShortcut: (): Promise<void> => ipcRenderer.invoke('finish:desktopShortcut'),
|
||||
startServer: (): Promise<void> => ipcRenderer.invoke('finish:startServer'),
|
||||
startMinecraftLauncher: (): Promise<void> => ipcRenderer.invoke('finish:startLauncher'),
|
||||
|
||||
// log stream
|
||||
onLog: (handler: (line: string) => void): (() => void) => {
|
||||
const listener = (_event: unknown, line: string) => handler(line)
|
||||
ipcRenderer.on('log', listener)
|
||||
return () => ipcRenderer.removeListener('log', listener)
|
||||
}
|
||||
}
|
||||
|
||||
contextBridge.exposeInMainWorld('installer', api)
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
installer: typeof api
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user