Skip to content

๐Ÿ—๏ธ ็ณป็ปŸๆžถๆž„่ฎพ่ฎก โ€‹

voidVM ้‡‡็”จๅ‰ๅŽ็ซฏๅˆ†็ฆป็š„็ŽฐไปฃๅŒ–ๆžถๆž„๏ผŒ้€š่ฟ‡ๅพฎๆœๅŠก่ฎพ่ฎกๅฎž็Žฐ้ซ˜ๅฏ็”จใ€ๅฏๆ‰ฉๅฑ•็š„่™šๆ‹Ÿๆœบ็ฎก็†ๅนณๅฐใ€‚

ๆ•ดไฝ“ๆžถๆž„ๅ›พ โ€‹

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚   Web Browser   โ”‚    โ”‚   Mobile App    โ”‚    โ”‚   Desktop App   โ”‚
โ”‚    (Vue)        โ”‚    โ”‚    (Future)     โ”‚    โ”‚    (Future)     โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
          โ”‚                      โ”‚                      โ”‚
          โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                 โ”‚
                    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                    โ”‚      Load Balancer         โ”‚
                    โ”‚       (Nginx)              โ”‚
                    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                 โ”‚
          โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
          โ”‚                      โ”‚                      โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚   API Gateway   โ”‚    โ”‚   Static Files  โ”‚    โ”‚   WebSocket     โ”‚
โ”‚   (Express.js)  โ”‚    โ”‚    (Assets)     โ”‚    โ”‚   (Socket.io)   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
          โ”‚                                             โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    Backend Services                            โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”             โ”‚
โ”‚  โ”‚    VM       โ”‚  โ”‚   Auth      โ”‚  โ”‚ Monitoring  โ”‚             โ”‚
โ”‚  โ”‚  Service    โ”‚  โ”‚  Service    โ”‚  โ”‚  Service    โ”‚             โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜             โ”‚
โ”‚                                                                โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”             โ”‚
โ”‚  โ”‚   Image     โ”‚  โ”‚   Network   โ”‚  โ”‚   Storage   โ”‚             โ”‚
โ”‚  โ”‚  Service    โ”‚  โ”‚  Service    โ”‚  โ”‚  Service    โ”‚             โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜             โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                 โ”‚
          โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
          โ”‚                      โ”‚                      โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚   Supabase      โ”‚    โ”‚     QEMU        โ”‚    โ”‚    Host OS      โ”‚
โ”‚  (Database)     โ”‚    โ”‚  Hypervisor     โ”‚    โ”‚   Resources     โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

ๅ‰็ซฏๆžถๆž„ (Vue) โ€‹

Frontend Architecture
โ”œโ”€โ”€ src/
โ”‚   โ”œโ”€โ”€ components/          # ๅฏๅค็”จ็ป„ไปถ
โ”‚   โ”‚   โ”œโ”€โ”€ common/         # ้€š็”จ็ป„ไปถ
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ Loading.vue
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ Modal.vue
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ Toast.vue
โ”‚   โ”‚   โ”œโ”€โ”€ vm/             # ่™šๆ‹Ÿๆœบ็›ธๅ…ณ็ป„ไปถ
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ VMCard.vue
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ VMConsole.vue
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ VMMetrics.vue
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ VMSettings.vue
โ”‚   โ”‚   โ””โ”€โ”€ layout/         # ๅธƒๅฑ€็ป„ไปถ
โ”‚   โ”‚       โ”œโ”€โ”€ Header.vue
โ”‚   โ”‚       โ”œโ”€โ”€ Sidebar.vue
โ”‚   โ”‚       โ””โ”€โ”€ Footer.vue
โ”‚   โ”œโ”€โ”€ views/              # ้กต้ข่ง†ๅ›พ
โ”‚   โ”‚   โ”œโ”€โ”€ Dashboard.vue
โ”‚   โ”‚   โ”œโ”€โ”€ VMList.vue
โ”‚   โ”‚   โ”œโ”€โ”€ VMDetail.vue
โ”‚   โ”‚   โ””โ”€โ”€ Settings.vue
โ”‚   โ”œโ”€โ”€ stores/             # Pinia ็Šถๆ€็ฎก็†
โ”‚   โ”‚   โ”œโ”€โ”€ auth.js
โ”‚   โ”‚   โ”œโ”€โ”€ vm.js
โ”‚   โ”‚   โ””โ”€โ”€ ui.js
โ”‚   โ”œโ”€โ”€ composables/        # ็ป„ๅˆๅผๅ‡ฝๆ•ฐ
โ”‚   โ”‚   โ”œโ”€โ”€ useAuth.js
โ”‚   โ”‚   โ”œโ”€โ”€ useVM.js
โ”‚   โ”‚   โ””โ”€โ”€ useWebSocket.js
โ”‚   โ”œโ”€โ”€ services/           # API ๆœๅŠกๅฑ‚
โ”‚   โ”‚   โ”œโ”€โ”€ api.js
โ”‚   โ”‚   โ”œโ”€โ”€ vmService.js
โ”‚   โ”‚   โ””โ”€โ”€ authService.js
โ”‚   โ””โ”€โ”€ utils/              # ๅทฅๅ…ทๅ‡ฝๆ•ฐ
โ”‚       โ”œโ”€โ”€ constants.js
โ”‚       โ”œโ”€โ”€ helpers.js
โ”‚       โ””โ”€โ”€ validators.js

ๅ‰็ซฏๆŠ€ๆœฏ้€‰ๅž‹:

  • Vue: ้‡‡็”จ Composition API๏ผŒๆไพ›ๆ›ดๅฅฝ็š„้€ป่พ‘ๅค็”จๅ’Œ็ฑปๅž‹ๆŽจๆ–ญ
  • Vue Router: ๅ•้กต้ขๅบ”็”จ่ทฏ็”ฑ็ฎก็†
  • Pinia: ็ŽฐไปฃๅŒ–็Šถๆ€็ฎก็†๏ผŒๆ›ฟไปฃ Vuex
  • Vite: ๅฟซ้€Ÿ็š„ๆž„ๅปบๅทฅๅ…ทๅ’Œๅผ€ๅ‘ๆœๅŠกๅ™จ
  • BootStrap: UI ็ป„ไปถๅบ“
  • Socket.io Client: ๅฎžๆ—ถ้€šไฟก

ๅŽ็ซฏๆžถๆž„ (Node.js) โ€‹

Backend Architecture
โ”œโ”€โ”€ server/
โ”‚   โ”œโ”€โ”€ routes/              # ่ทฏ็”ฑๅฑ‚
โ”‚   โ”‚   โ”œโ”€โ”€ auth.js         # ่ฎค่ฏ่ทฏ็”ฑ
โ”‚   โ”‚   โ”œโ”€โ”€ vms.js          # ่™šๆ‹Ÿๆœบ่ทฏ็”ฑ
โ”‚   โ”‚   โ”œโ”€โ”€ images.js       # ้•œๅƒ่ทฏ็”ฑ
โ”‚   โ”‚   โ””โ”€โ”€ monitoring.js   # ็›‘ๆŽง่ทฏ็”ฑ
โ”‚   โ”œโ”€โ”€ controllers/         # ๆŽงๅˆถๅ™จๅฑ‚
โ”‚   โ”‚   โ”œโ”€โ”€ AuthController.js
โ”‚   โ”‚   โ”œโ”€โ”€ VMController.js
โ”‚   โ”‚   โ”œโ”€โ”€ ImageController.js
โ”‚   โ”‚   โ””โ”€โ”€ MonitoringController.js
โ”‚   โ”œโ”€โ”€ services/            # ไธšๅŠก้€ป่พ‘ๅฑ‚
โ”‚   โ”‚   โ”œโ”€โ”€ VMService.js     # ่™šๆ‹ŸๆœบๆœๅŠก
โ”‚   โ”‚   โ”œโ”€โ”€ QEMUService.js   # QEMU ็ฎก็†ๆœๅŠก
โ”‚   โ”‚   โ”œโ”€โ”€ AuthService.js   # ่ฎค่ฏๆœๅŠก
โ”‚   โ”‚   โ”œโ”€โ”€ ImageService.js  # ้•œๅƒ็ฎก็†ๆœๅŠก
โ”‚   โ”‚   โ”œโ”€โ”€ NetworkService.js # ็ฝ‘็ปœ็ฎก็†ๆœๅŠก
โ”‚   โ”‚   โ””โ”€โ”€ StorageService.js # ๅญ˜ๅ‚จ็ฎก็†ๆœๅŠก
โ”‚   โ”œโ”€โ”€ middleware/          # ไธญ้—ดไปถ
โ”‚   โ”‚   โ”œโ”€โ”€ auth.js         # ่ฎค่ฏไธญ้—ดไปถ
โ”‚   โ”‚   โ”œโ”€โ”€ validation.js   # ๅ‚ๆ•ฐ้ชŒ่ฏ
โ”‚   โ”‚   โ”œโ”€โ”€ rateLimiter.js  # ้™ๆตไธญ้—ดไปถ
โ”‚   โ”‚   โ””โ”€โ”€ errorHandler.js # ้”™่ฏฏๅค„็†
โ”‚   โ”œโ”€โ”€ models/              # ๆ•ฐๆฎๆจกๅž‹
โ”‚   โ”‚   โ”œโ”€โ”€ VM.js
โ”‚   โ”‚   โ”œโ”€โ”€ User.js
โ”‚   โ”‚   โ””โ”€โ”€ Image.js
โ”‚   โ”œโ”€โ”€ utils/               # ๅทฅๅ…ท็ฑป
โ”‚   โ”‚   โ”œโ”€โ”€ qemuWrapper.js  # QEMU ๅ‘ฝไปคๅฐ่ฃ…
โ”‚   โ”‚   โ”œโ”€โ”€ logger.js       # ๆ—ฅๅฟ—ๅทฅๅ…ท
โ”‚   โ”‚   โ””โ”€โ”€ validators.js   # ้ชŒ่ฏๅทฅๅ…ท
โ”‚   โ””โ”€โ”€ config/              # ้…็ฝฎๆ–‡ไปถ
โ”‚       โ”œโ”€โ”€ database.js     # ๆ•ฐๆฎๅบ“้…็ฝฎ
โ”‚       โ”œโ”€โ”€ qemu.js         # QEMU ้…็ฝฎ
โ”‚       โ””โ”€โ”€ supabase.js     # Supabase ้…็ฝฎ

ๅŽ็ซฏๆŠ€ๆœฏ้€‰ๅž‹:

  • Express.js: Web ๆก†ๆžถ
  • Socket.io: WebSocket ๅฎžๆ—ถ้€šไฟก
  • Joi: ๅ‚ๆ•ฐ้ชŒ่ฏ
  • Winston: ๆ—ฅๅฟ—่ฎฐๅฝ•
  • Node-cron: ๅฎšๆ—ถไปปๅŠก
  • Multer: ๆ–‡ไปถไธŠไผ ๅค„็†

ๆ•ฐๆฎๅบ“่ฎพ่ฎก (Supabase/PostgreSQL) โ€‹

sql
-- ็”จๆˆท่กจ
CREATE TABLE users (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    email VARCHAR(255) UNIQUE NOT NULL,
    username VARCHAR(100) UNIQUE NOT NULL,
    avatar_url TEXT,
    role VARCHAR(20) DEFAULT 'user',
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- ่™šๆ‹Ÿๆœบ่กจ
CREATE TABLE virtual_machines (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    user_id UUID REFERENCES users(id) ON DELETE CASCADE,
    name VARCHAR(100) NOT NULL,
    description TEXT,
    status VARCHAR(20) DEFAULT 'stopped', -- stopped, running, paused, error
    cpu_cores INTEGER DEFAULT 1,
    memory_mb INTEGER DEFAULT 1024,
    disk_size_gb INTEGER DEFAULT 20,
    os_type VARCHAR(50),
    image_id UUID REFERENCES vm_images(id),
    network_config JSONB,
    qemu_config JSONB,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- ่™šๆ‹Ÿๆœบ้•œๅƒ่กจ
CREATE TABLE vm_images (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    name VARCHAR(100) NOT NULL,
    description TEXT,
    os_type VARCHAR(50),
    version VARCHAR(50),
    file_path TEXT NOT NULL,
    file_size_bytes BIGINT,
    checksum VARCHAR(64),
    is_public BOOLEAN DEFAULT false,
    created_by UUID REFERENCES users(id),
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- ่™šๆ‹Ÿๆœบ็›‘ๆŽงๆ•ฐๆฎ่กจ
CREATE TABLE vm_metrics (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    vm_id UUID REFERENCES virtual_machines(id) ON DELETE CASCADE,
    cpu_usage DECIMAL(5,2),
    memory_usage DECIMAL(5,2),
    disk_usage DECIMAL(5,2),
    network_in_bytes BIGINT,
    network_out_bytes BIGINT,
    timestamp TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- ๆ“ไฝœๆ—ฅๅฟ—่กจ
CREATE TABLE operation_logs (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    user_id UUID REFERENCES users(id),
    vm_id UUID REFERENCES virtual_machines(id),
    operation VARCHAR(50), -- create, start, stop, delete, etc.
    status VARCHAR(20), -- success, failed, pending
    details JSONB,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

QEMU ้›†ๆˆๆžถๆž„ โ€‹

javascript
// QEMU ๆœๅŠกๆŠฝ่ฑกๅฑ‚
class QEMUService {
  constructor() {
    this.runningVMs = new Map() // ่ฟ่กŒไธญ็š„่™šๆ‹Ÿๆœบๅฎžไพ‹
    this.qmpSockets = new Map() // QMP ็›‘ๆŽงๅฅ—ๆŽฅๅญ—
  }

  // ๅˆ›ๅปบ่™šๆ‹Ÿๆœบ
  async createVM(vmConfig) {
    const qemuArgs = this.buildQEMUArgs(vmConfig)
    const vmProcess = spawn('qemu-system-x86_64', qemuArgs)

    this.runningVMs.set(vmConfig.id, vmProcess)
    this.setupQMPConnection(vmConfig.id)

    return vmProcess
  }

  // ๆž„ๅปบ QEMU ๅ‚ๆ•ฐ
  buildQEMUArgs(config) {
    return [
      '-m',
      `${config.memory}M`,
      '-smp',
      `cores=${config.cpu}`,
      '-hda',
      config.diskPath,
      '-netdev',
      'user,id=net0',
      '-device',
      'e1000,netdev=net0',
      '-vnc',
      `:${config.vncPort}`,
      '-qmp',
      `unix:${config.qmpSocket},server,nowait`,
      config.cdrom ? ['-cdrom', config.cdrom] : [],
    ]
      .flat()
      .filter(Boolean)
  }

  // QMP ็›‘ๆŽง่ฟžๆŽฅ
  setupQMPConnection(vmId) {
    // ้€š่ฟ‡ QMP ๅ่ฎฎ็›‘ๆŽง่™šๆ‹Ÿๆœบ็Šถๆ€
    // ่Žทๅ– CPUใ€ๅ†…ๅญ˜ใ€็ฝ‘็ปœไฝฟ็”จๆƒ…ๅ†ต
  }
}

ๅฎžๆ—ถ้€šไฟกๆžถๆž„ โ€‹

javascript
// WebSocket ๆœๅŠก
class WebSocketService {
  constructor(server) {
    this.io = socketIO(server, {
      cors: { origin: '*' },
    })

    this.setupEventHandlers()
  }

  setupEventHandlers() {
    this.io.on('connection', socket => {
      // ็”จๆˆท่ฎค่ฏ
      socket.on('authenticate', async token => {
        const user = await this.verifyToken(token)
        socket.userId = user.id
        socket.join(`user:${user.id}`)
      })

      // ่ฎข้˜…่™šๆ‹Ÿๆœบ็Šถๆ€
      socket.on('subscribe:vm', vmId => {
        socket.join(`vm:${vmId}`)
      })

      // ่™šๆ‹Ÿๆœบๆ“ไฝœ
      socket.on('vm:start', async vmId => {
        try {
          await VMService.startVM(vmId)
          this.io.to(`vm:${vmId}`).emit('vm:status', {
            vmId,
            status: 'starting',
          })
        } catch (error) {
          socket.emit('error', error.message)
        }
      })
    })
  }

  // ๅนฟๆ’ญ่™šๆ‹Ÿๆœบ็Šถๆ€ๆ›ดๆ–ฐ
  broadcastVMStatus(vmId, status, metrics) {
    this.io.to(`vm:${vmId}`).emit('vm:status', {
      vmId,
      status,
      metrics,
      timestamp: new Date().toISOString(),
    })
  }
}

ๅฎ‰ๅ…จๆžถๆž„ โ€‹

javascript
// ๅฎ‰ๅ…จไธญ้—ดไปถ
const securityMiddleware = {
  // JWT ่ฎค่ฏ
  authenticate: async (req, res, next) => {
    const token = req.headers.authorization?.split(' ')[1]
    const user = await supabase.auth.getUser(token)
    req.user = user
    next()
  },

  // ๆƒ้™ๆฃ€ๆŸฅ
  authorize: permissions => {
    return (req, res, next) => {
      if (!req.user.permissions.includes(permissions)) {
        return res.status(403).json({ error: 'Forbidden' })
      }
      next()
    }
  },

  // ่ต„ๆบ่ฎฟ้—ฎๆŽงๅˆถ
  checkVMOwnership: async (req, res, next) => {
    const vmId = req.params.id
    const vm = await VMService.getVM(vmId)

    if (vm.user_id !== req.user.id && req.user.role !== 'admin') {
      return res.status(403).json({ error: 'Access denied' })
    }
    next()
  },

  // ้™ๆต
  rateLimit: rateLimit({
    windowMs: 15 * 60 * 1000, // 15 minutes
    max: 100, // limit each IP to 100 requests per windowMs
    message: 'Too many requests from this IP',
  }),

  // ่พ“ๅ…ฅ้ชŒ่ฏๅ’Œๆธ…็†
  validateInput: schema => {
    return (req, res, next) => {
      const { error, value } = schema.validate(req.body)
      if (error) {
        return res.status(400).json({ error: error.details[0].message })
      }
      req.body = value
      next()
    }
  },

  // CSRF ไฟๆŠค
  csrfProtection: csrf({
    cookie: {
      httpOnly: true,
      secure: process.env.NODE_ENV === 'production',
      sameSite: 'strict',
    },
  }),
}

็›‘ๆŽงไธŽๆ—ฅๅฟ—ๆžถๆž„ โ€‹

javascript
// ็›‘ๆŽงๆœๅŠก
class MonitoringService {
  constructor() {
    this.metrics = new Map()
    this.startMetricsCollection()
  }

  // ๆ”ถ้›†็ณป็ปŸๆŒ‡ๆ ‡
  async collectSystemMetrics() {
    return {
      cpu: await this.getCPUUsage(),
      memory: await this.getMemoryUsage(),
      disk: await this.getDiskUsage(),
      network: await this.getNetworkStats(),
      timestamp: new Date().toISOString(),
    }
  }

  // ๆ”ถ้›†่™šๆ‹ŸๆœบๆŒ‡ๆ ‡
  async collectVMMetrics(vmId) {
    const vm = this.runningVMs.get(vmId)
    if (!vm) return null

    try {
      // ้€š่ฟ‡ QMP ๅ่ฎฎ่Žทๅ–่™šๆ‹ŸๆœบๆŒ‡ๆ ‡
      const qmpClient = this.qmpSockets.get(vmId)
      const metrics = await qmpClient.query({
        execute: 'query-status',
      })

      return {
        vmId,
        status: metrics.status,
        cpu: await this.getVMCPUUsage(vmId),
        memory: await this.getVMMemoryUsage(vmId),
        disk: await this.getVMDiskUsage(vmId),
        network: await this.getVMNetworkStats(vmId),
        timestamp: new Date().toISOString(),
      }
    } catch (error) {
      logger.error(`Failed to collect metrics for VM ${vmId}:`, error)
      return null
    }
  }

  // ๅฏๅŠจๆŒ‡ๆ ‡ๆ”ถ้›†ๅฎšๆ—ถไปปๅŠก
  startMetricsCollection() {
    // ๆฏ30็ง’ๆ”ถ้›†ไธ€ๆฌก็ณป็ปŸๆŒ‡ๆ ‡
    cron.schedule('*/30 * * * * *', async () => {
      const metrics = await this.collectSystemMetrics()
      await this.storeMetrics('system', metrics)
    })

    // ๆฏ15็ง’ๆ”ถ้›†ไธ€ๆฌก่™šๆ‹ŸๆœบๆŒ‡ๆ ‡
    cron.schedule('*/15 * * * * *', async () => {
      for (const vmId of this.runningVMs.keys()) {
        const metrics = await this.collectVMMetrics(vmId)
        if (metrics) {
          await this.storeMetrics('vm', metrics)
          // ้€š่ฟ‡ WebSocket ๅนฟๆ’ญๅฎžๆ—ถๆŒ‡ๆ ‡
          wsService.broadcastVMMetrics(vmId, metrics)
        }
      }
    })
  }

  // ๅญ˜ๅ‚จๆŒ‡ๆ ‡ๅˆฐๆ•ฐๆฎๅบ“
  async storeMetrics(type, metrics) {
    try {
      if (type === 'vm') {
        await supabase.from('vm_metrics').insert({
          vm_id: metrics.vmId,
          cpu_usage: metrics.cpu,
          memory_usage: metrics.memory,
          disk_usage: metrics.disk,
          network_in_bytes: metrics.network.in,
          network_out_bytes: metrics.network.out,
          timestamp: metrics.timestamp,
        })
      }
    } catch (error) {
      logger.error('Failed to store metrics:', error)
    }
  }
}

// ๆ—ฅๅฟ—ๆœๅŠก
const logger = winston.createLogger({
  level: 'info',
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.errors({ stack: true }),
    winston.format.json()
  ),
  defaultMeta: { service: 'voidVM' },
  transports: [
    // ้”™่ฏฏๆ—ฅๅฟ—ๅ†™ๅ…ฅๆ–‡ไปถ
    new winston.transports.File({
      filename: 'logs/error.log',
      level: 'error',
    }),
    // ๆ‰€ๆœ‰ๆ—ฅๅฟ—ๅ†™ๅ…ฅๆ–‡ไปถ
    new winston.transports.File({
      filename: 'logs/combined.log',
    }),
    // ๅผ€ๅ‘็Žฏๅขƒ่พ“ๅ‡บๅˆฐๆŽงๅˆถๅฐ
    ...(process.env.NODE_ENV !== 'production'
      ? [
          new winston.transports.Console({
            format: winston.format.simple(),
          }),
        ]
      : []),
  ],
})

็ผ“ๅญ˜ๆžถๆž„ โ€‹

javascript
// Redis ็ผ“ๅญ˜ๆœๅŠก
class CacheService {
  constructor() {
    this.redis = new Redis({
      host: process.env.REDIS_HOST || 'localhost',
      port: process.env.REDIS_PORT || 6379,
      password: process.env.REDIS_PASSWORD,
      db: 0,
    })
  }

  // ็ผ“ๅญ˜่™šๆ‹Ÿๆœบ็Šถๆ€
  async cacheVMStatus(vmId, status, ttl = 300) {
    const key = `vm:status:${vmId}`
    await this.redis.setex(key, ttl, JSON.stringify(status))
  }

  // ่Žทๅ–็ผ“ๅญ˜็š„่™šๆ‹Ÿๆœบ็Šถๆ€
  async getCachedVMStatus(vmId) {
    const key = `vm:status:${vmId}`
    const cached = await this.redis.get(key)
    return cached ? JSON.parse(cached) : null
  }

  // ็ผ“ๅญ˜็”จๆˆทไผš่ฏ
  async cacheUserSession(userId, sessionData, ttl = 3600) {
    const key = `session:${userId}`
    await this.redis.setex(key, ttl, JSON.stringify(sessionData))
  }

  // ๆธ…้™ค่™šๆ‹Ÿๆœบ็›ธๅ…ณ็ผ“ๅญ˜
  async clearVMCache(vmId) {
    const pattern = `vm:*:${vmId}`
    const keys = await this.redis.keys(pattern)
    if (keys.length > 0) {
      await this.redis.del(...keys)
    }
  }
}

้ƒจ็ฝฒๆžถๆž„ โ€‹

yaml
# docker-compose.yml
version: '3.8'

services:
  # ๅ‰็ซฏๆœๅŠก
  frontend:
    build:
      context: ./client
      dockerfile: Dockerfile
    ports:
      - '3000:80'
    environment:
      - VITE_API_URL=http://localhost:5000
      - VITE_SUPABASE_URL=${SUPABASE_URL}
      - VITE_SUPABASE_ANON_KEY=${SUPABASE_ANON_KEY}
    depends_on:
      - backend

  # ๅŽ็ซฏๆœๅŠก
  backend:
    build:
      context: ./server
      dockerfile: Dockerfile
    ports:
      - '5000:5000'
    environment:
      - NODE_ENV=production
      - SUPABASE_URL=${SUPABASE_URL}
      - SUPABASE_SERVICE_KEY=${SUPABASE_SERVICE_KEY}
      - REDIS_HOST=redis
    volumes:
      - /var/lib/libvirt:/var/lib/libvirt
      - /dev/kvm:/dev/kvm
    privileged: true
    depends_on:
      - redis
      - postgres

  # Redis ็ผ“ๅญ˜
  redis:
    image: redis:7-alpine
    ports:
      - '6379:6379'
    volumes:
      - redis_data:/data
    command: redis-server --appendonly yes

  # PostgreSQL (ๅค‡็”จ๏ผŒไธป่ฆไฝฟ็”จ Supabase)
  postgres:
    image: postgres:15
    environment:
      - POSTGRES_DB=voidvm
      - POSTGRES_USER=${DB_USER}
      - POSTGRES_PASSWORD=${DB_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - '5432:5432'

  # Nginx ๅๅ‘ไปฃ็†
  nginx:
    image: nginx:alpine
    ports:
      - '80:80'
      - '443:443'
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
      - ./ssl:/etc/nginx/ssl
    depends_on:
      - frontend
      - backend

volumes:
  redis_data:
  postgres_data:

ๆ€ง่ƒฝไผ˜ๅŒ–ๆžถๆž„ โ€‹

javascript
// ๆ€ง่ƒฝไผ˜ๅŒ–ๆœๅŠก
class PerformanceService {
  constructor() {
    this.vmQueue = new Queue('vm-operations', {
      redis: {
        host: process.env.REDIS_HOST,
        port: process.env.REDIS_PORT,
      },
    })

    this.setupQueueProcessors()
  }

  // ่ฎพ็ฝฎ้˜Ÿๅˆ—ๅค„็†ๅ™จ
  setupQueueProcessors() {
    // ่™šๆ‹ŸๆœบๅฏๅŠจ้˜Ÿๅˆ—
    this.vmQueue.process('start-vm', 5, async job => {
      const { vmId, userId } = job.data
      return await VMService.startVM(vmId, userId)
    })

    // ่™šๆ‹Ÿๆœบๅœๆญข้˜Ÿๅˆ—
    this.vmQueue.process('stop-vm', 10, async job => {
      const { vmId, userId } = job.data
      return await VMService.stopVM(vmId, userId)
    })

    // ้•œๅƒๆž„ๅปบ้˜Ÿๅˆ—
    this.vmQueue.process('build-image', 2, async job => {
      const { imageConfig } = job.data
      return await ImageService.buildImage(imageConfig)
    })
  }

  // ๅผ‚ๆญฅๆ‰ง่กŒ่™šๆ‹Ÿๆœบๆ“ไฝœ
  async queueVMOperation(operation, vmId, userId) {
    const job = await this.vmQueue.add(
      `${operation}-vm`,
      {
        vmId,
        userId,
        timestamp: new Date().toISOString(),
      },
      {
        attempts: 3,
        backoff: {
          type: 'exponential',
          delay: 2000,
        },
      }
    )

    return job.id
  }

  // ่ฟžๆŽฅๆฑ ็ฎก็†
  setupConnectionPool() {
    // ๆ•ฐๆฎๅบ“่ฟžๆŽฅๆฑ 
    this.dbPool = new Pool({
      connectionString: process.env.DATABASE_URL,
      max: 20,
      idleTimeoutMillis: 30000,
      connectionTimeoutMillis: 2000,
    })

    // QEMU ่ฟ›็จ‹ๆฑ 
    this.qemuPool = new Map()
  }
}

็พ้šพๆขๅคๆžถๆž„ โ€‹

javascript
// ๅค‡ไปฝๆขๅคๆœๅŠก
class BackupService {
  constructor() {
    this.setupScheduledBackups()
  }

  // ่™šๆ‹Ÿๆœบๅฟซ็…ง
  async createVMSnapshot(vmId, snapshotName) {
    try {
      logger.info(`Creating snapshot ${snapshotName} for VM ${vmId}`)

      const vm = await VMService.getVM(vmId)
      const qmpClient = this.qmpSockets.get(vmId)

      // ๅˆ›ๅปบๅ†…ๅญ˜ๅฟซ็…ง
      await qmpClient.execute('savevm', {
        name: snapshotName,
      })

      // ๅˆ›ๅปบ็ฃ็›˜ๅฟซ็…ง
      await this.createDiskSnapshot(vm.disk_path, snapshotName)

      // ่ฎฐๅฝ•ๅฟซ็…งไฟกๆฏ
      await supabase.from('vm_snapshots').insert({
        vm_id: vmId,
        name: snapshotName,
        type: 'full',
        created_at: new Date().toISOString(),
      })

      logger.info(`Snapshot ${snapshotName} created successfully`)
      return true
    } catch (error) {
      logger.error(`Failed to create snapshot: ${error.message}`)
      throw error
    }
  }

  // ๆขๅค่™šๆ‹Ÿๆœบๅฟซ็…ง
  async restoreVMSnapshot(vmId, snapshotName) {
    try {
      logger.info(`Restoring snapshot ${snapshotName} for VM ${vmId}`)

      // ๅœๆญข่™šๆ‹Ÿๆœบ
      await VMService.stopVM(vmId)

      // ๆขๅค็ฃ็›˜
      await this.restoreDiskSnapshot(vmId, snapshotName)

      // ๅฏๅŠจ่™šๆ‹ŸๆœบๅนถๅŠ ่ฝฝๅ†…ๅญ˜ๅฟซ็…ง
      await VMService.startVM(vmId)
      const qmpClient = this.qmpSockets.get(vmId)
      await qmpClient.execute('loadvm', {
        name: snapshotName,
      })

      logger.info(`Snapshot ${snapshotName} restored successfully`)
      return true
    } catch (error) {
      logger.error(`Failed to restore snapshot: ${error.message}`)
      throw error
    }
  }

  // ๅฎšๆ—ถๅค‡ไปฝ
  setupScheduledBackups() {
    // ๆฏๅคฉๅ‡Œๆ™จ2็‚นๆ‰ง่กŒ่‡ชๅŠจๅค‡ไปฝ
    cron.schedule('0 2 * * *', async () => {
      const runningVMs = await VMService.getRunningVMs()

      for (const vm of runningVMs) {
        try {
          const snapshotName = `auto_${new Date().toISOString().split('T')[0]}`
          await this.createVMSnapshot(vm.id, snapshotName)
        } catch (error) {
          logger.error(`Auto backup failed for VM ${vm.id}: ${error.message}`)
        }
      }
    })
  }
}

API ่ฎพ่ฎก่ง„่Œƒ โ€‹

javascript
// RESTful API ่ฎพ่ฎก
const apiRoutes = {
    // ่™šๆ‹Ÿๆœบ็ฎก็†
    'GET /api/v1/vms': 'List all VMs for authenticated user',
    'POST /api/v1/vms': 'Create a new VM',
    'GET /api/v1/vms/:id': 'Get VM details',
    'PUT /api/v1/vms/:id': 'Update VM configuration',
    'DELETE /api/v1/vms/:id': 'Delete VM',

    // ่™šๆ‹Ÿๆœบๆ“ไฝœ
    'POST /api/v1/vms/:id/start': 'Start VM',
    'POST /api/v1/vms/:id/stop': 'Stop VM',
    'POST /api/v1/vms/:id/pause': 'Pause VM',
    'POST /api/v1/vms/:id/resume': 'Resume VM',
    'POST /api/v1/vms/:id/reset': 'Reset VM',

    // ๅฟซ็…ง็ฎก็†
    'GET /api/v1/vms/:id/snapshots': 'List VM snapshots',
    'POST /api/v1/vms/:id/snapshots': 'Create VM snapshot',
    'POST /api/v1/vms/:id/snapshots/:name/restore': 'Restore snapshot',
    'DELETE /api/v1/vms/:id/snapshots/:name
    'DELETE /api/v1/vms/:id/snapshots/:name': 'Delete snapshot',

    // ็›‘ๆŽงๅ’ŒๆŒ‡ๆ ‡
    'GET /api/v1/vms/:id/metrics': 'Get VM performance metrics',
    'GET /api/v1/vms/:id/console': 'Get VM console access',
    'GET /api/v1/vms/:id/logs': 'Get VM operation logs',

    // ้•œๅƒ็ฎก็†
    'GET /api/v1/images': 'List available VM images',
    'POST /api/v1/images': 'Upload new VM image',
    'GET /api/v1/images/:id': 'Get image details',
    'DELETE /api/v1/images/:id': 'Delete VM image',

    // ็ฝ‘็ปœ็ฎก็†
    'GET /api/v1/networks': 'List virtual networks',
    'POST /api/v1/networks': 'Create virtual network',
    'GET /api/v1/networks/:id': 'Get network details',
    'PUT /api/v1/networks/:id': 'Update network configuration',
    'DELETE /api/v1/networks/:id': 'Delete virtual network',

    // ๅญ˜ๅ‚จ็ฎก็†
    'GET /api/v1/storage': 'List storage pools',
    'POST /api/v1/storage': 'Create storage pool',
    'GET /api/v1/storage/:id/volumes': 'List volumes in storage pool',
    'POST /api/v1/storage/:id/volumes': 'Create new volume',

    // ็”จๆˆท็ฎก็†
    'GET /api/v1/users/profile': 'Get user profile',
    'PUT /api/v1/users/profile': 'Update user profile',
    'GET /api/v1/users/usage': 'Get resource usage statistics',

    // ็ณป็ปŸ็ฎก็†
    'GET /api/v1/system/stats': 'Get system statistics',
    'GET /api/v1/system/health': 'Health check endpoint',
    'GET /api/v1/system/version': 'Get system version info'
};

// API ๅ“ๅบ”ๆ ผๅผๆ ‡ๅ‡†
const ApiResponse = {
    success: {
        code: 200,
        message: 'Success',
        data: {},
        timestamp: new Date().toISOString()
    },
    error: {
        code: 400,
        message: 'Error message',
        error: 'Detailed error information',
        timestamp: new Date().toISOString()
    }
};

ๆ‰ฉๅฑ•ๆ€งๆžถๆž„ โ€‹

javascript
// ๅพฎๆœๅŠกๆžถๆž„ๆ‰ฉๅฑ•
class MicroserviceArchitecture {
  constructor() {
    this.services = new Map()
    this.serviceRegistry = new ServiceRegistry()
    this.loadBalancer = new LoadBalancer()
  }

  // ๆœๅŠกๆณจๅ†Œ
  registerService(serviceName, serviceInstance) {
    this.services.set(serviceName, serviceInstance)
    this.serviceRegistry.register(serviceName, serviceInstance)
  }

  // ๆœๅŠกๅ‘็Žฐ
  async discoverService(serviceName) {
    return await this.serviceRegistry.discover(serviceName)
  }

  // ่ดŸ่ฝฝๅ‡่กก
  async routeRequest(serviceName, request) {
    const serviceInstances = await this.discoverService(serviceName)
    const selectedInstance = this.loadBalancer.select(serviceInstances)
    return await selectedInstance.handleRequest(request)
  }
}

// ๆœๅŠกๆณจๅ†Œไธญๅฟƒ
class ServiceRegistry {
  constructor() {
    this.services = new Map()
    this.healthChecker = new HealthChecker()
  }

  register(serviceName, instance) {
    if (!this.services.has(serviceName)) {
      this.services.set(serviceName, [])
    }
    this.services.get(serviceName).push(instance)

    // ๅฏๅŠจๅฅๅบทๆฃ€ๆŸฅ
    this.healthChecker.monitor(instance)
  }

  async discover(serviceName) {
    const instances = this.services.get(serviceName) || []
    // ่ฟ”ๅ›žๅฅๅบท็š„ๆœๅŠกๅฎžไพ‹
    return instances.filter(instance => instance.isHealthy)
  }
}

// ๅฎนๅ™จๅŒ–ๆ‰ฉๅฑ•
class ContainerOrchestration {
  constructor() {
    this.docker = new Docker()
    this.kubernetes = new KubernetesClient()
  }

  // Docker ๅฎนๅ™จ็ฎก็†
  async deployService(serviceName, config) {
    const container = await this.docker.createContainer({
      Image: config.image,
      name: serviceName,
      Env: config.environment,
      PortBindings: config.ports,
      RestartPolicy: { Name: 'unless-stopped' },
    })

    await container.start()
    return container
  }

  // Kubernetes ้ƒจ็ฝฒ
  async deployToK8s(serviceName, manifest) {
    await this.kubernetes.apply(manifest)
    return await this.kubernetes.waitForDeployment(serviceName)
  }
}

ๅฎ‰ๅ…จๅŠ ๅ›บๆžถๆž„ โ€‹

javascript
// ๅฎ‰ๅ…จๆœๅŠก
class SecurityService {
  constructor() {
    this.setupSecurityPolicies()
    this.auditLogger = new AuditLogger()
  }

  // ่ฎพ็ฝฎๅฎ‰ๅ…จ็ญ–็•ฅ
  setupSecurityPolicies() {
    // ๅฏ†็ ็ญ–็•ฅ
    this.passwordPolicy = {
      minLength: 8,
      requireUppercase: true,
      requireLowercase: true,
      requireNumbers: true,
      requireSpecialChars: true,
      maxAge: 90, // days
    }

    // ไผš่ฏ็ญ–็•ฅ
    this.sessionPolicy = {
      maxAge: 3600, // 1 hour
      maxConcurrentSessions: 5,
      requireMFA: false,
    }

    // ่ฎฟ้—ฎๆŽงๅˆถ็ญ–็•ฅ
    this.accessPolicy = {
      maxFailedAttempts: 5,
      lockoutDuration: 1800, // 30 minutes
      allowedIPs: [], // empty = allow all
      blockedIPs: [],
    }
  }

  // ๅคšๅ› ็ด ่ฎค่ฏ
  async setupMFA(userId, method = 'totp') {
    const secret = authenticator.generateSecret()
    const qrCode = await QRCode.toDataURL(authenticator.keyuri(userId, 'voidVM', secret))

    await supabase.from('user_mfa').insert({
      user_id: userId,
      method,
      secret: this.encrypt(secret),
      enabled: false,
    })

    return { secret, qrCode }
  }

  // ้ชŒ่ฏ MFA
  async verifyMFA(userId, token) {
    const mfaRecord = await supabase
      .from('user_mfa')
      .select('secret')
      .eq('user_id', userId)
      .single()

    if (!mfaRecord) {
      throw new Error('MFA not configured')
    }

    const secret = this.decrypt(mfaRecord.secret)
    return authenticator.verify({ token, secret })
  }

  // ๅฎก่ฎกๆ—ฅๅฟ—
  async logSecurityEvent(event) {
    await this.auditLogger.log({
      type: 'security',
      event: event.type,
      userId: event.userId,
      ip: event.ip,
      userAgent: event.userAgent,
      details: event.details,
      timestamp: new Date().toISOString(),
    })
  }

  // ๅŠ ๅฏ†่งฃๅฏ†
  encrypt(text) {
    const cipher = crypto.createCipher('aes-256-cbc', process.env.ENCRYPTION_KEY)
    let encrypted = cipher.update(text, 'utf8', 'hex')
    encrypted += cipher.final('hex')
    return encrypted
  }

  decrypt(encryptedText) {
    const decipher = crypto.createDecipher('aes-256-cbc', process.env.ENCRYPTION_KEY)
    let decrypted = decipher.update(encryptedText, 'hex', 'utf8')
    decrypted += decipher.final('utf8')
    return decrypted
  }
}

ๆต‹่ฏ•ๆžถๆž„ โ€‹

javascript
// ๆต‹่ฏ•ๆก†ๆžถ
describe('voidVM Test Suite', () => {
  // ๅ•ๅ…ƒๆต‹่ฏ•
  describe('Unit Tests', () => {
    describe('VM Service', () => {
      it('should create a new VM', async () => {
        const vmConfig = {
          name: 'test-vm',
          cpu: 2,
          memory: 2048,
          disk: 20,
        }

        const vm = await VMService.createVM(vmConfig)
        expect(vm).to.have.property('id')
        expect(vm.name).to.equal('test-vm')
      })

      it('should start a VM', async () => {
        const vmId = 'test-vm-id'
        const result = await VMService.startVM(vmId)
        expect(result).to.be.true
      })
    })

    describe('QEMU Service', () => {
      it('should build correct QEMU arguments', () => {
        const config = {
          memory: 2048,
          cpu: 2,
          diskPath: '/path/to/disk.qcow2',
        }

        const args = QEMUService.buildQEMUArgs(config)
        expect(args).to.include('-m')
        expect(args).to.include('2048M')
      })
    })
  })

  // ้›†ๆˆๆต‹่ฏ•
  describe('Integration Tests', () => {
    describe('API Endpoints', () => {
      it('should create VM via API', async () => {
        const response = await request(app)
          .post('/api/v1/vms')
          .set('Authorization', `Bearer ${testToken}`)
          .send({
            name: 'integration-test-vm',
            cpu: 1,
            memory: 1024,
          })

        expect(response.status).to.equal(201)
        expect(response.body.data).to.have.property('id')
      })
    })

    describe('Database Operations', () => {
      it('should store VM configuration', async () => {
        const vm = await VMService.createVM(testVMConfig)
        const stored = await supabase.from('virtual_machines').select('*').eq('id', vm.id).single()

        expect(stored.data).to.not.be.null
      })
    })
  })

  // ็ซฏๅˆฐ็ซฏๆต‹่ฏ•
  describe('E2E Tests', () => {
    describe('VM Lifecycle', () => {
      it('should complete full VM lifecycle', async () => {
        // ๅˆ›ๅปบ่™šๆ‹Ÿๆœบ
        const vm = await VMService.createVM(testVMConfig)

        // ๅฏๅŠจ่™šๆ‹Ÿๆœบ
        await VMService.startVM(vm.id)
        expect(await VMService.getVMStatus(vm.id)).to.equal('running')

        // ๅœๆญข่™šๆ‹Ÿๆœบ
        await VMService.stopVM(vm.id)
        expect(await VMService.getVMStatus(vm.id)).to.equal('stopped')

        // ๅˆ ้™ค่™šๆ‹Ÿๆœบ
        await VMService.deleteVM(vm.id)
        const deleted = await VMService.getVM(vm.id)
        expect(deleted).to.be.null
      })
    })
  })

  // ๆ€ง่ƒฝๆต‹่ฏ•
  describe('Performance Tests', () => {
    it('should handle concurrent VM operations', async () => {
      const promises = []
      const vmCount = 10

      for (let i = 0; i < vmCount; i++) {
        promises.push(
          VMService.createVM({
            name: `perf-test-vm-${i}`,
            cpu: 1,
            memory: 512,
          })
        )
      }

      const results = await Promise.all(promises)
      expect(results).to.have.length(vmCount)
    })

    it('should respond to API requests within acceptable time', async () => {
      const start = Date.now()

      await request(app).get('/api/v1/vms').set('Authorization', `Bearer ${testToken}`)

      const duration = Date.now() - start
      expect(duration).to.be.below(1000) // less than 1 second
    })
  })
})

้…็ฝฎ็ฎก็†ๆžถๆž„ โ€‹

javascript
// ้…็ฝฎ็ฎก็†
class ConfigManager {
  constructor() {
    this.configs = new Map()
    this.loadConfigurations()
  }

  loadConfigurations() {
    // ๅบ”็”จ้…็ฝฎ
    this.configs.set('app', {
      name: 'voidVM',
      version: process.env.APP_VERSION || '1.0.0',
      environment: process.env.NODE_ENV || 'development',
      port: process.env.PORT || 5000,
      host: process.env.HOST || 'localhost',
    })

    // ๆ•ฐๆฎๅบ“้…็ฝฎ
    this.configs.set('database', {
      supabase: {
        url: process.env.SUPABASE_URL,
        anonKey: process.env.SUPABASE_ANON_KEY,
        serviceKey: process.env.SUPABASE_SERVICE_KEY,
      },
      redis: {
        host: process.env.REDIS_HOST || 'localhost',
        port: process.env.REDIS_PORT || 6379,
        password: process.env.REDIS_PASSWORD,
      },
    })

    // QEMU ้…็ฝฎ
    this.configs.set('qemu', {
      binaryPath: process.env.QEMU_BINARY || '/usr/bin/qemu-system-x86_64',
      imagePath: process.env.VM_IMAGE_PATH || '/var/lib/voidvm/images',
      maxConcurrentVMs: process.env.MAX_CONCURRENT_VMS || 10,
      defaultMemory: 1024,
      defaultCPU: 1,
      vncPortRange: {
        start: 5900,
        end: 5999,
      },
    })

    // ๅฎ‰ๅ…จ้…็ฝฎ
    this.configs.set('security', {
      jwtSecret: process.env.JWT_SECRET,
      encryptionKey: process.env.ENCRYPTION_KEY,
      sessionTimeout: 3600,
      maxLoginAttempts: 5,
      lockoutDuration: 1800,
    })

    // ็›‘ๆŽง้…็ฝฎ
    this.configs.set('monitoring', {
      metricsInterval: 30000, // 30 seconds
      logLevel: process.env.LOG_LEVEL || 'info',
      enableMetrics: process.env.ENABLE_METRICS !== 'false',
      alertingEnabled: process.env.ALERTING_ENABLED === 'true',
    })
  }

  get(configName, key = null) {
    const config = this.configs.get(configName)
    if (!config) {
      throw new Error(`Configuration '${configName}' not found`)
    }

    return key ? config[key] : config
  }

  validate() {
    const required = [
      'SUPABASE_URL',
      'SUPABASE_ANON_KEY',
      'SUPABASE_SERVICE_KEY',
      'JWT_SECRET',
      'ENCRYPTION_KEY',
    ]

    const missing = required.filter(key => !process.env[key])

    if (missing.length > 0) {
      throw new Error(`Missing required environment variables: ${missing.join(', ')}`)
    }

    // ้ชŒ่ฏ QEMU ไบŒ่ฟ›ๅˆถๆ–‡ไปถๅญ˜ๅœจ
    if (!fs.existsSync(this.get('qemu', 'binaryPath'))) {
      throw new Error('QEMU binary not found at specified path')
    }

    // ้ชŒ่ฏ้•œๅƒ็›ฎๅฝ•ๅญ˜ๅœจๆˆ–ๅˆ›ๅปบ
    const imagePath = this.get('qemu', 'imagePath')
    if (!fs.existsSync(imagePath)) {
      fs.mkdirSync(imagePath, { recursive: true })
    }

    return true
  }
}

ๆ•ฐๆฎๆตๆžถๆž„ โ€‹

็‚นๅ‡ปๆ”พๅคง

้”™่ฏฏๅค„็†ๆžถๆž„ โ€‹

javascript
// ็ปŸไธ€้”™่ฏฏๅค„็†
class ErrorHandler {
  constructor() {
    this.errorTypes = {
      VALIDATION_ERROR: 'ValidationError',
      AUTHENTICATION_ERROR: 'AuthenticationError',
      AUTHORIZATION_ERROR: 'AuthorizationError',
      RESOURCE_NOT_FOUND: 'ResourceNotFoundError',
      QEMU_ERROR: 'QEMUError',
      DATABASE_ERROR: 'DatabaseError',
      NETWORK_ERROR: 'NetworkError',
      SYSTEM_ERROR: 'SystemError',
    }
  }

  // ๅˆ›ๅปบ่‡ชๅฎšไน‰้”™่ฏฏ็ฑป
  createError(type, message, details = {}) {
    const error = new Error(message)
    error.name = type
    error.details = details
    error.timestamp = new Date().toISOString()
    return error
  }

  // Express ้”™่ฏฏๅค„็†ไธญ้—ดไปถ
  expressErrorHandler() {
    return (err, req, res, next) => {
      // ่ฎฐๅฝ•้”™่ฏฏ
      logger.error({
        error: err.message,
        stack: err.stack,
        url: req.url,
        method: req.method,
        userId: req.user?.id,
        timestamp: new Date().toISOString(),
      })

      // ๆ นๆฎ้”™่ฏฏ็ฑปๅž‹่ฟ”ๅ›žไธๅŒ็š„ๅ“ๅบ”
      let statusCode = 500
      let message = 'Internal Server Error'

      switch (err.name) {
        case this.errorTypes.VALIDATION_ERROR:
          statusCode = 400
          message = err.message
          break
        case this.errorTypes.AUTHENTICATION_ERROR:
          statusCode = 401
          message = 'Authentication required'
          break
        case this.errorTypes.AUTHORIZATION_ERROR:
          statusCode = 403
          message = 'Access denied'
          break
        case this.errorTypes.RESOURCE_NOT_FOUND:
          statusCode = 404
          message = err.message || 'Resource not found'
          break
        case this.errorTypes.QEMU_ERROR:
          statusCode = 500
          message = 'Virtual machine operation failed'
          break
      }

      res.status(statusCode).json({
        error: true,
        message,
        code: err.name,
        details: process.env.NODE_ENV === 'development' ? err.details : undefined,
        timestamp: new Date().toISOString(),
      })
    }
  }

  // ๅผ‚ๆญฅๆ“ไฝœ้”™่ฏฏๅค„็†
  asyncWrapper(fn) {
    return (req, res, next) => {
      Promise.resolve(fn(req, res, next)).catch(next)
    }
  }

  // QEMU ้”™่ฏฏๅค„็†
  handleQEMUError(error, vmId) {
    const qemuError = this.createError(
      this.errorTypes.QEMU_ERROR,
      `QEMU operation failed for VM ${vmId}`,
      {
        vmId,
        originalError: error.message,
        stderr: error.stderr,
      }
    )

    logger.error('QEMU Error:', qemuError)
    return qemuError
  }
}

Released under the MIT License.