+ 안녕하세요, 님! +
+서버를 관리하고 사용량을 확인하세요
+서버 목록을 불러오는 중...
+아직 서버가 없습니다
+첫 번째 서버를 생성해보세요!
+ +
+ 서버별 비용 상세
+월별 비용 추이
+알림
+ +새로운 알림이 없습니다
+서버 상태 변경 시 알림을 받을 수 있습니다
+diff --git a/app.js b/app.js index 2ee2cfc..62ffe8c 100644 --- a/app.js +++ b/app.js @@ -66,6 +66,52 @@ const DEPLOY_TIMING = { COMPLETE: 6000 }; +// Mock 데이터 (API 없이 UI 테스트용) +const MOCK_SERVERS = [ + { + id: 'srv-001', + name: 'production-web', + region: '🇯🇵 Tokyo', + ip: '45.12.89.101', + os: 'Debian 12', + plan: 'Pro', + status: 'running', + created_at: '2026-01-15', + vcpu: '2 Cores', + ram: '4 GB', + cost: 40700 + }, + { + id: 'srv-002', + name: 'dev-api', + region: '🇰🇷 Seoul', + ip: '45.12.89.102', + os: 'Ubuntu 24.04', + plan: 'Starter', + status: 'stopped', + created_at: '2026-01-20', + vcpu: '1 Core', + ram: '2 GB', + cost: 17000 + } +]; + +const MOCK_STATS = { + totalCost: 57700, + totalServers: 2, + runningServers: 1, + costBreakdown: [ + { plan: 'Pro', count: 1, cost: 40700 }, + { plan: 'Starter', count: 1, cost: 17000 } + ] +}; + +const MOCK_NOTIFICATIONS = [ + { id: 'n-001', type: 'info', title: '서버 생성 완료', message: 'production-web 서버가 Tokyo 리전에 생성되었습니다.', time: '10분 전', read: false }, + { id: 'n-002', type: 'warning', title: '결제 예정', message: '이번 달 결제가 3일 후 예정되어 있습니다. (₩57,700)', time: '1시간 전', read: false }, + { id: 'n-003', type: 'success', title: '시스템 업데이트 완료', message: 'dev-api 서버의 Ubuntu 패키지가 업데이트되었습니다.', time: '2일 전', read: true } +]; + /** * 가격 포맷팅 (한국 원화) */ @@ -84,14 +130,46 @@ function anvilApp() { wizardStep: 0, // 0: region, 1: plan, 2: os, 3: payment, 4: confirm, 5+: deploying deployStep: 0, + // 대시보드 상태 + dashboardMode: false, // true면 대시보드, false면 랜딩 + currentView: 'servers', // 'servers' | 'stats' | 'notifications' + + // 텔레그램 연동 + telegram: { + isAvailable: false, // 텔레그램 환경인지 + user: null, // 사용자 정보 + initData: null // 검증용 데이터 + }, + // 서버 구성 config: { region: null, plan: null, os: null, - payment: null + payment: null, + telegram_id: null }, + // 서버 목록 + servers: [], + loadingServers: false, + + // 통계 + stats: { + totalCost: 0, + totalServers: 0, + runningServers: 0, + costBreakdown: [] + }, + + // 알림 + notifications: [], + unreadCount: 0, + + // API 상태 + apiLoading: false, + apiError: null, + // 대화 메시지 messages: [], @@ -108,6 +186,188 @@ function anvilApp() { return ['Micro', 'Starter', 'Pro', 'Business']; }, + // 초기화 (텔레그램 연동 + 대시보드) + init() { + if (window.Telegram?.WebApp) { + const tg = window.Telegram.WebApp; + tg.ready(); + tg.expand(); + + this.telegram.isAvailable = true; + this.telegram.user = tg.initDataUnsafe.user || null; + this.telegram.initData = tg.initData; + + // 텔레그램 테마 색상 적용 (선택) + // document.body.style.backgroundColor = tg.backgroundColor; + + console.log('[Telegram] Mini App initialized', { + user: this.telegram.user, + platform: tg.platform + }); + + // 텔레그램 환경이면 대시보드 모드 활성화 + // (user가 없어도 텔레그램에서 열린 경우 대시보드 표시) + this.dashboardMode = true; + this.loadDashboard(); + + console.log('[Telegram] Dashboard mode activated', { + isAvailable: this.telegram.isAvailable, + hasUser: !!this.telegram.user + }); + } else { + console.log('[Telegram] Running in web browser mode'); + } + }, + + // 대시보드 초기 로드 + async loadDashboard() { + console.log('[Dashboard] Loading dashboard data...'); + await Promise.all([ + this.fetchServers(), + this.fetchStats(), + this.fetchNotifications() + ]); + }, + + // 서버 목록 조회 + async fetchServers() { + this.loadingServers = true; + this.apiError = null; + + try { + // TODO: 실제 API 호출로 교체 + // const response = await fetch('/api/servers', { + // headers: { 'X-Telegram-Init-Data': this.telegram.initData } + // }); + // this.servers = await response.json(); + + // Mock 데이터 사용 + await new Promise(resolve => setTimeout(resolve, 500)); + this.servers = [...MOCK_SERVERS]; + console.log('[Dashboard] Servers loaded:', this.servers.length); + } catch (error) { + console.error('[Dashboard] Failed to fetch servers:', error); + this.apiError = '서버 목록을 불러오는데 실패했습니다.'; + } finally { + this.loadingServers = false; + } + }, + + // 통계 조회 + async fetchStats() { + try { + // TODO: 실제 API 호출로 교체 + await new Promise(resolve => setTimeout(resolve, 300)); + this.stats = { ...MOCK_STATS }; + console.log('[Dashboard] Stats loaded:', this.stats); + } catch (error) { + console.error('[Dashboard] Failed to fetch stats:', error); + } + }, + + // 알림 조회 + async fetchNotifications() { + try { + // TODO: 실제 API 호출로 교체 + await new Promise(resolve => setTimeout(resolve, 400)); + this.notifications = [...MOCK_NOTIFICATIONS]; + this.unreadCount = this.notifications.filter(n => !n.read).length; + console.log('[Dashboard] Notifications loaded:', this.notifications.length); + } catch (error) { + console.error('[Dashboard] Failed to fetch notifications:', error); + } + }, + + // 서버 시작 + async startServer(serverId) { + const server = this.servers.find(s => s.id === serverId); + if (!server) return; + + console.log('[Dashboard] Starting server:', serverId); + this.apiLoading = true; + + try { + // TODO: 실제 API 호출로 교체 + await new Promise(resolve => setTimeout(resolve, 1000)); + server.status = 'running'; + this.stats.runningServers++; + console.log('[Dashboard] Server started:', serverId); + } catch (error) { + console.error('[Dashboard] Failed to start server:', error); + alert('서버를 시작하는데 실패했습니다.'); + } finally { + this.apiLoading = false; + } + }, + + // 서버 중지 + async stopServer(serverId) { + const server = this.servers.find(s => s.id === serverId); + if (!server) return; + + console.log('[Dashboard] Stopping server:', serverId); + this.apiLoading = true; + + try { + // TODO: 실제 API 호출로 교체 + await new Promise(resolve => setTimeout(resolve, 1000)); + server.status = 'stopped'; + this.stats.runningServers--; + console.log('[Dashboard] Server stopped:', serverId); + } catch (error) { + console.error('[Dashboard] Failed to stop server:', error); + alert('서버를 중지하는데 실패했습니다.'); + } finally { + this.apiLoading = false; + } + }, + + // 서버 삭제 + async deleteServer(serverId) { + if (!confirm('정말로 이 서버를 삭제하시겠습니까?')) return; + + console.log('[Dashboard] Deleting server:', serverId); + this.apiLoading = true; + + try { + // TODO: 실제 API 호출로 교체 + await new Promise(resolve => setTimeout(resolve, 800)); + + const serverIndex = this.servers.findIndex(s => s.id === serverId); + if (serverIndex !== -1) { + const deletedServer = this.servers[serverIndex]; + this.servers.splice(serverIndex, 1); + + // 통계 업데이트 + this.stats.totalServers--; + this.stats.totalCost -= deletedServer.cost; + if (deletedServer.status === 'running') { + this.stats.runningServers--; + } + + console.log('[Dashboard] Server deleted:', serverId); + } + } catch (error) { + console.error('[Dashboard] Failed to delete server:', error); + alert('서버를 삭제하는데 실패했습니다.'); + } finally { + this.apiLoading = false; + } + }, + + // 모든 알림 읽음 처리 + markAllRead() { + this.notifications.forEach(n => n.read = true); + this.unreadCount = 0; + console.log('[Dashboard] All notifications marked as read'); + }, + + // 뷰 전환 + switchView(view) { + this.currentView = view; + console.log('[Dashboard] View switched to:', view); + }, + // 가격 조회 getPrice(plan) { const prices = LAUNCHER_PRICES[plan]; @@ -192,6 +452,11 @@ function anvilApp() { // 서버 배포 시작 startLaunch() { + // 텔레그램 사용자 ID 추가 + if (this.telegram.user) { + this.config.telegram_id = this.telegram.user.id; + } + this.launching = true; this.wizardStep = 5; this.deployStep = 1; @@ -201,6 +466,9 @@ function anvilApp() { `📦 ${this.config.os} 이미지 준비 중...` ]; + // 디버그: config 출력 + console.log('[Server Launch] Config:', this.config); + setTimeout(() => { this.logs.push('✅ 이미지 준비 완료'); this.logs.push('🔧 컨테이너 인스턴스 생성 중...'); @@ -224,13 +492,26 @@ function anvilApp() { this.launching = false; this.deployStep = 5; this.logs.push('🎉 서버가 활성화되었습니다!'); + + // 대시보드 모드에서는 서버 목록 새로고침 + if (this.dashboardMode) { + setTimeout(() => { + this.fetchServers(); + this.fetchStats(); + }, 1000); + } }, DEPLOY_TIMING.COMPLETE); }, - // 런처 열기 + // 런처 열기 (대시보드용 추가 기능) openLauncher() { this.launcherOpen = true; this.messages = [{ type: 'bot', text: '어느 리전에 서버를 생성할까요?', time: new Date() }]; + + // 대시보드 모드에서는 서버 생성 후 목록 새로고침 + if (this.dashboardMode) { + console.log('[Dashboard] Launcher opened from dashboard'); + } }, // 런처 초기화 @@ -239,7 +520,7 @@ function anvilApp() { this.launching = false; this.wizardStep = 0; this.deployStep = 0; - this.config = { region: null, plan: null, os: null, payment: null }; + this.config = { region: null, plan: null, os: null, payment: null, telegram_id: null }; this.messages = []; this.logs = []; }, diff --git a/index.html b/index.html index e723aeb..2be00c1 100644 --- a/index.html +++ b/index.html @@ -3,7 +3,7 @@
- +서버를 관리하고 사용량을 확인하세요
+서버 목록을 불러오는 중...
+첫 번째 서버를 생성해보세요!
+ +
+ 서버 상태 변경 시 알림을 받을 수 있습니다
+