package main //go:generate bash -c "cd app && bun install && bun run build && sed -i 's/\"elysia\": \"^1.3.21\",/\"@sveltejs\\/kit\": \"^2.22.0\",\\n\\t\\t\"elysia\": \"^1.3.21\",/' build/package.json" import ( "bufio" "embed" "fmt" "io" "io/fs" "log" "os" "os/exec" "os/signal" "path/filepath" "runtime" "syscall" ) //go:embed all:app/build var buildFiles embed.FS func main() { port := os.Getenv("PORT") if port == "" { port = "3000" } homeDir, err := os.UserHomeDir() if err != nil { log.Fatal("Failed to get home directory:", err) } bunPath := filepath.Join(homeDir, ".bun", "bin", "bun") fmt.Printf("Looking for Bun at: %s\n", bunPath) if _, err := os.Stat(bunPath); os.IsNotExist(err) { fmt.Println("Bun runtime not found. Installing Bun...") if err := installBun(); err != nil { log.Fatal("Failed to install Bun:", err) } if _, err := os.Stat(bunPath); os.IsNotExist(err) { log.Fatal("Bun installation failed - binary not found at:", bunPath) } fmt.Printf("Bun installed successfully at: %s\n", bunPath) } else { fmt.Printf("Found existing Bun at: %s\n", bunPath) } tempDir, err := os.MkdirTemp("", "craftstation-*") if err != nil { log.Fatal("Failed to create temp directory:", err) } defer os.RemoveAll(tempDir) buildFS, err := fs.Sub(buildFiles, "app/build") if err != nil { log.Fatal("Failed to create sub filesystem:", err) } err = extractFiles(buildFS, tempDir) if err != nil { log.Fatal("Failed to extract files:", err) } buildPath := tempDir fmt.Println("Installing dependencies...") installCmd := exec.Command(bunPath, "install") installCmd.Dir = buildPath installCmd.Stdout = os.Stdout installCmd.Stderr = os.Stderr if err := installCmd.Run(); err != nil { log.Fatal("Failed to install dependencies:", err) } cmd := exec.Command(bunPath, "index.js") cmd.Dir = buildPath cmd.Env = append(os.Environ(), "PORT="+port, "NODE_ENV=production", "FORCE_COLOR=1", "BUN_LOG_LEVEL=info", ) stdout, err := cmd.StdoutPipe() if err != nil { log.Fatal("Failed to create stdout pipe:", err) } stderr, err := cmd.StderrPipe() if err != nil { log.Fatal("Failed to create stderr pipe:", err) } fmt.Printf("[Runtime] Craftstation server starting on port %s\n", port) fmt.Printf("[Runtime] Open http://localhost:%s in your browser\n", port) sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) go func() { <-sigChan fmt.Println("\nShutting down server...") if cmd.Process != nil { cmd.Process.Signal(syscall.SIGTERM) } os.Exit(0) }() if err := cmd.Start(); err != nil { log.Fatal("Failed to start server:", err) } go func() { scanner := bufio.NewScanner(stdout) for scanner.Scan() { fmt.Printf("[Webserver] %s\n", scanner.Text()) } }() go func() { scanner := bufio.NewScanner(stderr) for scanner.Scan() { fmt.Printf("[Webserver Error] %s\n", scanner.Text()) } }() if err := cmd.Wait(); err != nil { if exitErr, ok := err.(*exec.ExitError); ok { if status, ok := exitErr.Sys().(syscall.WaitStatus); ok { os.Exit(status.ExitStatus()) } } log.Fatal("Server process failed:", err) } } func installBun() error { fmt.Println("Installing Bun runtime...") var cmd *exec.Cmd switch runtime.GOOS { case "windows": cmd = exec.Command("powershell", "-c", "irm bun.sh/install.ps1 | iex") case "darwin", "linux": cmd = exec.Command("bash", "-c", "curl -fsSL https://bun.sh/install | bash") default: return fmt.Errorf("unsupported operating system: %s", runtime.GOOS) } cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { return fmt.Errorf("failed to run Bun installer: %w", err) } fmt.Println("Bun installation completed!") return nil } func extractFiles(fsys fs.FS, destDir string) error { return fs.WalkDir(fsys, ".", func(path string, d fs.DirEntry, err error) error { if err != nil { return err } destPath := filepath.Join(destDir, path) if d.IsDir() { return os.MkdirAll(destPath, 0755) } file, err := fsys.Open(path) if err != nil { return err } defer file.Close() destFile, err := os.Create(destPath) if err != nil { return err } defer destFile.Close() _, err = io.Copy(destFile, file) return err }) }