Files
Craftstation/bin.go
2025-09-08 02:22:33 +02:00

190 lines
4.2 KiB
Go

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
})
}