Files
wehub-resource-sync f99010fae1
Desktop Artifacts / Desktop Build (Linux) (push) Waiting to run
Desktop Artifacts / Desktop Build (Windows) (push) Waiting to run
Desktop Artifacts / Desktop Build (Linux (arm64)) (push) Waiting to run
Desktop Artifacts (macOS) / Desktop Build (macOS (aarch64)) (push) Waiting to run
Desktop Artifacts (macOS) / Desktop Build (macOS (x86_64)) (push) Waiting to run
CI / lint (push) Failing after 1s
CI / frontend (push) Failing after 1s
CI / scripts (push) Failing after 1s
CI / Go Test (ubuntu-latest) (push) Failing after 0s
CI / frontend-node-25 (push) Failing after 1s
CI / docs (push) Failing after 0s
CI / coverage (push) Failing after 0s
CI / e2e (push) Failing after 0s
Docker / build-and-push (push) Failing after 1s
CI / integration (push) Failing after 4m43s
CI / Go Test (windows-latest) (push) Has been cancelled
CI / Desktop Unit Tests (Windows) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:30:36 +08:00

200 lines
4.4 KiB
Go

package main
import (
"context"
"errors"
"fmt"
"net/http"
"time"
"go.kenn.io/agentsview/internal/config"
"go.kenn.io/agentsview/internal/server"
)
type serveRuntimeOptions struct {
Mode string
RequestedPort int
}
type serveRuntime struct {
Cfg config.Config
LocalURL string
PublicURL string
ServeErrCh <-chan error
Caddy *managedCaddy
}
func prepareServeRuntimeConfig(
cfg config.Config,
opts serveRuntimeOptions,
) (config.Config, error) {
requestedPort := opts.RequestedPort
if requestedPort == 0 {
requestedPort = cfg.Port
}
port := server.FindAvailablePort(cfg.Host, cfg.Port)
if port != cfg.Port {
if cfg.Port == 0 {
fmt.Printf("Using available port %d\n", port)
} else {
fmt.Printf("Port %d in use, using %d\n", cfg.Port, port)
}
}
cfg.Port = port
if cfg.Proxy.Mode == "" && cfg.PublicURL != "" {
updatedURL, updatedOrigins, changed, err := rewriteConfiguredPublicURLPort(
cfg.PublicURL,
cfg.PublicOrigins,
requestedPort,
cfg.Port,
)
if err != nil {
return cfg, fmt.Errorf("invalid public url: %w", err)
}
if changed {
cfg.PublicURL = updatedURL
cfg.PublicOrigins = updatedOrigins
}
}
return cfg, nil
}
func startServerWithOptionalCaddy(
ctx context.Context,
cfg config.Config,
srv *server.Server,
opts serveRuntimeOptions,
) (*serveRuntime, error) {
serveErrCh := make(chan error, 1)
go func() {
serveErrCh <- srv.ListenAndServe()
}()
if err := waitForLocalPort(
ctx, cfg.Host, cfg.Port, 5*time.Second, serveErrCh,
); err != nil {
shutdownCtx, cancel := context.WithTimeout(
context.Background(), 5*time.Second,
)
defer cancel()
_ = srv.Shutdown(shutdownCtx)
if errors.Is(err, context.Canceled) {
return nil, err
}
return nil, fmt.Errorf("server failed to start: %w", err)
}
var caddy *managedCaddy
if cfg.Proxy.Mode == "caddy" {
var err error
caddy, err = startManagedCaddy(ctx, cfg, opts.Mode)
if err != nil {
shutdownCtx, cancel := context.WithTimeout(
context.Background(), 5*time.Second,
)
defer cancel()
_ = srv.Shutdown(shutdownCtx)
return nil, fmt.Errorf("managed caddy error: %w", err)
}
publicPort, err := publicURLPort(cfg.PublicURL)
if err != nil {
shutdownCtx, cancel := context.WithTimeout(
context.Background(), 5*time.Second,
)
defer cancel()
caddy.Stop()
_ = srv.Shutdown(shutdownCtx)
return nil, fmt.Errorf("invalid public url: %w", err)
}
if err := waitForLocalPort(
ctx,
cfg.Proxy.BindHost,
publicPort,
5*time.Second,
caddy.Err(),
); err != nil {
shutdownCtx, cancel := context.WithTimeout(
context.Background(), 5*time.Second,
)
defer cancel()
caddy.Stop()
_ = srv.Shutdown(shutdownCtx)
if errors.Is(err, context.Canceled) {
return nil, err
}
return nil, fmt.Errorf("managed caddy error: %w", err)
}
}
return &serveRuntime{
Cfg: cfg,
LocalURL: fmt.Sprintf("http://%s:%d", cfg.Host, cfg.Port),
PublicURL: browserURL(cfg),
ServeErrCh: serveErrCh,
Caddy: caddy,
}, nil
}
func waitForServerRuntime(
ctx context.Context,
srv *server.Server,
rt *serveRuntime,
) error {
var caddyErrCh <-chan error
if rt.Caddy != nil {
caddyErrCh = rt.Caddy.Err()
}
select {
case err := <-rt.ServeErrCh:
if err != nil && err != http.ErrServerClosed {
if rt.Caddy != nil {
rt.Caddy.Stop()
}
return fmt.Errorf("server error: %w", err)
}
if rt.Caddy != nil {
rt.Caddy.Stop()
}
return nil
case err := <-caddyErrCh:
shutdownCtx, cancel := context.WithTimeout(
context.Background(), 5*time.Second,
)
defer cancel()
_ = srv.Shutdown(shutdownCtx)
if ctx.Err() != nil {
if serveErr := <-rt.ServeErrCh; serveErr != nil &&
serveErr != http.ErrServerClosed {
return fmt.Errorf("server error: %w", serveErr)
}
return nil
}
if err != nil {
return fmt.Errorf("managed caddy error: %w", err)
}
return fmt.Errorf("managed caddy exited unexpectedly")
case <-ctx.Done():
shutdownCtx, cancel := context.WithTimeout(
context.Background(), 5*time.Second,
)
defer cancel()
if rt.Caddy != nil {
rt.Caddy.Stop()
}
if err := srv.Shutdown(shutdownCtx); err != nil &&
err != http.ErrServerClosed {
return fmt.Errorf("server shutdown error: %w", err)
}
if err := <-rt.ServeErrCh; err != nil &&
err != http.ErrServerClosed {
return fmt.Errorf("server error: %w", err)
}
return nil
}
}