123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242 |
- package main
- import (
- "fmt"
- "log"
- "net/http"
- "os"
- "os/signal"
- "path/filepath"
- "strings"
- "syscall"
- "github.com/gorilla/csrf"
- "imuslab.com/dezukvm/dezukvmd/mod/auth"
- "imuslab.com/dezukvm/dezukvmd/mod/dezukvm"
- "imuslab.com/dezukvm/dezukvmd/mod/logger"
- "imuslab.com/dezukvm/dezukvmd/mod/utils"
- )
- var (
- dezukvmManager *dezukvm.DezukVM
- listeningServerMux *http.ServeMux
- authManager *auth.AuthManager
- systemLogger *logger.Logger
- )
- func init_auth_manager() error {
- // Initialize logger
- systemLogger = logger.NewLogger(logger.WithLogLevel(logger.InfoLevel))
- // Initialize AuthManager with logger and DB path
- var err error
- authManager, err = auth.NewAuthManager(auth.Options{
- DBPath: DB_FILE_PATH,
- Log: systemLogger.Info,
- })
- if err != nil {
- return err
- }
- return nil
- }
- func init_ipkvm_mode() error {
- listeningServerMux = http.NewServeMux()
- // Initialize the Auth Manager
- err := init_auth_manager()
- if err != nil {
- log.Fatal("Failed to initialize Auth Manager:", err)
- return err
- }
- //Create a new DezukVM manager
- dezukvmManager = dezukvm.NewKvmHostInstance(&dezukvm.RuntimeOptions{
- EnableLog: true,
- })
- // Experimental
- connectedUsbKvms, err := dezukvm.ScanConnectedUsbKvmDevices()
- if err != nil {
- return err
- }
- for _, dev := range connectedUsbKvms {
- err := dezukvmManager.AddUsbKvmDevice(dev)
- if err != nil {
- return err
- }
- }
- err = dezukvmManager.StartAllUsbKvmDevices()
- if err != nil {
- return err
- }
- // ~Experimental
- // Handle root routing with CSRF protection
- handle_root_routing(listeningServerMux)
- // Handle program exit to close the HID controller
- c := make(chan os.Signal, 1)
- signal.Notify(c, os.Interrupt, syscall.SIGTERM)
- go func() {
- <-c
- log.Println("Shutting down DezuKVM...")
- if dezukvmManager != nil {
- dezukvmManager.Close()
- }
- if authManager != nil {
- authManager.Close()
- }
- log.Println("Shutdown complete.")
- os.Exit(0)
- }()
- // Register Auth related APIs
- register_auth_apis(listeningServerMux)
- // Register DezukVM related APIs
- register_ipkvm_apis(listeningServerMux)
- err = http.ListenAndServe(":9000", listeningServerMux)
- return err
- }
- func request_url_allow_unauthenticated(r *http.Request) bool {
- // Define a list of URL paths that can be accessed without authentication
- allowedPaths := []string{
- "/login.html",
- "/api/v1/login",
- "/img/",
- "/js/",
- "/css/",
- "/favicon.png",
- }
- requestPath := r.URL.Path
- for _, path := range allowedPaths {
- if strings.HasPrefix(requestPath, path) {
- return true
- }
- }
- return false
- }
- func handle_root_routing(mux *http.ServeMux) {
- // Root router: check login status, redirect to login.html if not authenticated
- mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
- if !authManager.UserIsLoggedIn(r) && !request_url_allow_unauthenticated(r) {
- w.Header().Set("Location", "/login.html")
- w.WriteHeader(http.StatusFound)
- return
- }
- // Only inject for .html files
- path := r.URL.Path
- if path == "/" {
- path = "/index.html"
- }
- if strings.HasSuffix(path, ".html") {
- // Read the HTML file from disk
- targetFilePath := filepath.Join("www", filepath.Clean(path))
- content, err := os.ReadFile(targetFilePath)
- if err != nil {
- http.NotFound(w, r)
- return
- }
- htmlContent := string(content)
- // Replace CSRF token placeholder
- htmlContent = strings.ReplaceAll(htmlContent, "{{.csrfToken}}", csrf.Token(r))
- w.Header().Set("Content-Type", "text/html")
- w.Write([]byte(htmlContent))
- return
- }
- // Serve static files (img, js, css, etc.)
- targetFilePath := filepath.Join("www", filepath.Clean(path))
- http.ServeFile(w, r, targetFilePath)
- })
- }
- func register_auth_apis(mux *http.ServeMux) {
- // Check API for session validation
- mux.HandleFunc("/api/v1/check", func(w http.ResponseWriter, r *http.Request) {
- if r.Method != http.MethodPost {
- http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
- return
- }
- ok := authManager.UserIsLoggedIn(r)
- if !ok {
- http.Error(w, "Unauthorized", http.StatusUnauthorized)
- return
- }
- w.WriteHeader(http.StatusOK)
- w.Write([]byte("{\"status\":\"ok\"}"))
- })
- // Login API
- mux.HandleFunc("/api/v1/login", func(w http.ResponseWriter, r *http.Request) {
- if r.Method != http.MethodPost {
- http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
- return
- }
- err := authManager.LoginUser(w, r)
- if err != nil {
- http.Error(w, "Unauthorized", http.StatusUnauthorized)
- return
- }
- w.WriteHeader(http.StatusOK)
- w.Write([]byte("{\"status\":\"success\"}"))
- })
- }
- func register_ipkvm_apis(mux *http.ServeMux) {
- authManager.HandleFunc("/api/v1/stream/{uuid}/video", func(w http.ResponseWriter, r *http.Request) {
- instanceUUID := r.PathValue("uuid")
- fmt.Println("Requested video stream for instance UUID:", instanceUUID)
- dezukvmManager.HandleVideoStreams(w, r, instanceUUID)
- }, mux)
- authManager.HandleFunc("/api/v1/stream/{uuid}/audio", func(w http.ResponseWriter, r *http.Request) {
- instanceUUID := r.PathValue("uuid")
- dezukvmManager.HandleAudioStreams(w, r, instanceUUID)
- }, mux)
- authManager.HandleFunc("/api/v1/hid/{uuid}/events", func(w http.ResponseWriter, r *http.Request) {
- instanceUUID := r.PathValue("uuid")
- dezukvmManager.HandleHIDEvents(w, r, instanceUUID)
- }, mux)
- authManager.HandleFunc("/api/v1/mass_storage/switch", func(w http.ResponseWriter, r *http.Request) {
- if r.Method != http.MethodPost {
- http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
- return
- }
- instanceUUID, err := utils.PostPara(r, "uuid")
- if err != nil {
- http.Error(w, "Missing or invalid uuid parameter", http.StatusBadRequest)
- return
- }
- side, err := utils.PostPara(r, "side")
- if err != nil {
- http.Error(w, "Missing or invalid side parameter", http.StatusBadRequest)
- return
- }
- switch side {
- case "kvm":
- dezukvmManager.HandleMassStorageSideSwitch(w, r, instanceUUID, true)
- case "remote":
- dezukvmManager.HandleMassStorageSideSwitch(w, r, instanceUUID, false)
- default:
- http.Error(w, "Invalid side parameter", http.StatusBadRequest)
- }
- }, mux)
- authManager.HandleFunc("/api/v1/instances", func(w http.ResponseWriter, r *http.Request) {
- if r.Method == http.MethodGet {
- dezukvmManager.HandleListInstances(w, r)
- } else {
- http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
- }
- }, mux)
- }
|