/** * Server Recommendation Wizard * 서버 추천 마법사 로직 */ import { WIZARD_CONFIG } from './config.js'; /** * 서버 추천 로직 (룰 기반) * @param {string[]} selectedStacks - 선택된 스택 ID 목록 * @param {string} scale - 규모 키 (personal, small, medium, large) * @returns {Object} 추천 결과 { economy, recommended, performance } */ export function calculateRecommendation(selectedStacks, scale) { // 기본 요구사항 계산 let totalRam = 0; let totalCpu = 0; let needsGpu = false; selectedStacks.forEach(stackId => { // 모든 카테고리에서 스택 찾기 for (const category of Object.values(WIZARD_CONFIG.stacks)) { const stack = category.find(s => s.id === stackId); if (stack) { totalRam += stack.ram; totalCpu += stack.cpu; if (stack.gpu) needsGpu = true; break; } } }); // 규모에 따른 배율 적용 (이제 객체 형태) const scaleConfig = WIZARD_CONFIG.scales[scale]; const multiplier = scaleConfig?.multiplier || 1; totalRam = Math.ceil(totalRam * multiplier); totalCpu = Math.ceil(totalCpu * multiplier); // 최소/최대 제한 totalRam = Math.max(1024, Math.min(totalRam, 65536)); totalCpu = Math.max(1, Math.min(totalCpu, 16)); // 가격 계산 (대략적인 추정, 실제로는 API 호출 필요) const estimatePrice = (cpu, ram) => { // 기본 가격: vCPU당 $5, GB당 $2 (월간) return cpu * 5 + (ram / 1024) * 2; }; // 추천 플랜 결정 (객체 형태로 반환) return { economy: { cpu: Math.max(1, Math.floor(totalCpu * 0.7)), ram: Math.max(1024, Math.floor(totalRam * 0.7)), price: estimatePrice(Math.max(1, Math.floor(totalCpu * 0.7)), Math.max(1024, Math.floor(totalRam * 0.7))) }, recommended: { cpu: totalCpu, ram: totalRam, price: estimatePrice(totalCpu, totalRam) }, performance: { cpu: Math.min(16, Math.ceil(totalCpu * 1.5)), ram: Math.min(65536, Math.ceil(totalRam * 1.5)), price: estimatePrice(Math.min(16, Math.ceil(totalCpu * 1.5)), Math.min(65536, Math.ceil(totalRam * 1.5))) }, needsGpu, totalStacks: selectedStacks.length, scale }; } /** * 마법사 앱 상태 및 메서드 * anvilApp에서 사용할 마법사 관련 로직 */ export function createWizardMethods() { return { // 마법사 상태 wizardOpen: false, wizardCurrentStep: 0, // 0: purpose, 1: stack, 2: scale, 3: result wizardPurpose: null, wizardStacks: [], // 선택된 스택들 (다중 선택) wizardScale: null, wizardRecommendations: null, wizardSelectedPlan: null, // 마법사 데이터 접근자 get wizardPurposes() { return WIZARD_CONFIG.purposes; }, get wizardScales() { return WIZARD_CONFIG.scales; }, // 현재 용도에 맞는 스택 목록 get wizardAvailableStacks() { if (!this.wizardPurpose) return []; return WIZARD_CONFIG.stacks[this.wizardPurpose] || []; }, // 마법사 열기 openWizard() { this.wizardOpen = true; this.wizardCurrentStep = 0; this.wizardPurpose = null; this.wizardStacks = []; this.wizardScale = null; this.wizardRecommendations = null; this.wizardSelectedPlan = null; }, // 마법사 닫기 closeWizard() { this.wizardOpen = false; }, // 용도 선택 selectWizardPurpose(purposeId) { this.wizardPurpose = purposeId; this.wizardStacks = []; // 스택 초기화 this.wizardCurrentStep = 1; }, // 스택 토글 (다중 선택) toggleWizardStack(stackId) { const idx = this.wizardStacks.indexOf(stackId); if (idx === -1) { this.wizardStacks.push(stackId); } else { this.wizardStacks.splice(idx, 1); } }, // 스택 선택 완료 → 규모 선택으로 confirmWizardStacks() { if (this.wizardStacks.length === 0) { alert('최소 1개의 기술 스택을 선택해주세요.'); return; } this.wizardCurrentStep = 2; }, // 규모 선택 → 추천 결과로 selectWizardScale(scaleId) { this.wizardScale = scaleId; this.wizardRecommendations = calculateRecommendation(this.wizardStacks, scaleId); this.wizardCurrentStep = 3; }, // 이전 단계로 wizardGoBack() { if (this.wizardCurrentStep > 0) { this.wizardCurrentStep--; } }, // 추천 플랜 선택 → 텔레그램으로 연결 selectWizardPlan(tierKey) { const plan = this.wizardRecommendations[tierKey]; if (!plan) return; this.wizardSelectedPlan = plan; // 텔레그램 봇으로 연결 (선택한 사양 정보 포함) window.open(`https://t.me/AnvilForgeBot?start=wizard_${tierKey}_${plan.cpu}c_${plan.ram}m`, '_blank'); this.closeWizard(); }, // RAM 포맷팅 formatWizardRam(mb) { if (mb >= 1024) { return (mb / 1024).toFixed(mb % 1024 === 0 ? 0 : 1) + ' GB'; } return mb + ' MB'; }, // 현재 용도에 맞는 스택 그룹 반환 getWizardStacksByPurpose() { if (!this.wizardPurpose) return {}; const purposeStacks = WIZARD_CONFIG.stacks[this.wizardPurpose] || []; // 카테고리별로 그룹핑 const grouped = {}; for (const stack of purposeStacks) { if (!grouped[stack.category]) { grouped[stack.category] = []; } grouped[stack.category].push(stack); } return grouped; }, // 스택 ID로 이름 찾기 getWizardStackName(stackId) { for (const purposeStacks of Object.values(WIZARD_CONFIG.stacks)) { const stack = purposeStacks.find(s => s.id === stackId); if (stack) return stack.name; } return stackId; } }; }