package main import ( "context" "errors" "fmt" "log" "net/http" "os" "os/signal" "path/filepath" "syscall" "time" ) func main() { if err := run(); err != nil { log.Fatal(err) } } func run() error { root, err := repositoryRoot() if err != nil { return err } log.Print("building core and handles packages") packages, err := buildPackages(root) if err != nil { return fmt.Errorf("build packages: %w", err) } log.Printf("loaded %d package assets into memory", len(packages)) databasePath := os.Getenv("YAWN_DATABASE") if databasePath == "" { databasePath = filepath.Join(root, "server", "data", "yawn.db") } store, err := openPlaygroundStore(databasePath) if err != nil { return fmt.Errorf("open playground database: %w", err) } defer store.Close() app, err := newApplication( store, packages, filepath.Join(root, "node_modules", "monaco-editor", "min"), ) if err != nil { return err } port := os.Getenv("PORT") if port == "" { port = "8080" } server := &http.Server{ Addr: ":" + port, Handler: app.routes(), ReadHeaderTimeout: 5 * time.Second, ReadTimeout: 15 * time.Second, WriteTimeout: 30 * time.Second, IdleTimeout: 60 * time.Second, } stopped := make(chan os.Signal, 1) signal.Notify(stopped, syscall.SIGINT, syscall.SIGTERM) go func() { <-stopped ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() if err := server.Shutdown(ctx); err != nil { log.Printf("shutdown: %v", err) } }() log.Printf("Yawn server listening on http://0.0.0.0:%s", port) err = server.ListenAndServe() if errors.Is(err, http.ErrServerClosed) { return nil } return err } func repositoryRoot() (string, error) { directory, err := os.Getwd() if err != nil { return "", err } for { if fileExists(filepath.Join(directory, "Cargo.toml")) && fileExists(filepath.Join(directory, "core", "Cargo.toml")) && fileExists(filepath.Join(directory, "addons", "handles", "package.json")) { return directory, nil } parent := filepath.Dir(directory) if parent == directory { return "", errors.New("could not find the Yawn repository root") } directory = parent } } func fileExists(path string) bool { info, err := os.Stat(path) return err == nil && !info.IsDir() }