diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..793f543 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +docker-compose.yml +.gitignore +mkdocs.yml +README.md +docs +**tmp** \ No newline at end of file diff --git a/Dockerfile_fsd b/Dockerfile_fsd new file mode 100644 index 0000000..924ee84 --- /dev/null +++ b/Dockerfile_fsd @@ -0,0 +1,25 @@ +FROM golang:1.24 AS build + +WORKDIR /go/src/fsd +COPY go.mod go.sum ./ + +# Cache module downloads +RUN go mod download + +COPY . . + +# Cache builds +ENV GOCACHE=/root/.cache/go-build +RUN --mount=type=cache,target="/root/.cache/go-build" CGO_ENABLED=0 go build -o /go/bin/fsd + +FROM alpine:latest + +RUN addgroup -g 2001 nonroot && \ + adduser -u 2001 -G nonroot -D nonroot && \ + mkdir /db && chown -R nonroot:nonroot /db + +COPY --from=build --chown=nonroot:nonroot /go/bin/fsd / + +USER 2001:2001 + +CMD ["/fsd"] diff --git a/Dockerfile_web b/Dockerfile_web new file mode 100644 index 0000000..83577c6 --- /dev/null +++ b/Dockerfile_web @@ -0,0 +1,27 @@ +FROM golang:1.24 AS build + +WORKDIR /go/src/fsdweb +COPY go.mod go.sum ./ + +# Cache module downloads +RUN go mod download + +COPY . . + +# Cache builds +ENV GOCACHE=/root/.cache/go-build +RUN --mount=type=cache,target="/root/.cache/go-build" \ + cd web && \ + CGO_ENABLED=0 go build -o /go/bin/fsdweb + +FROM alpine:latest + +RUN addgroup -g 2001 nonroot && \ + adduser -u 2001 -G nonroot -D nonroot && \ + mkdir /db && chown -R nonroot:nonroot /db + +COPY --from=build --chown=nonroot:nonroot /go/bin/fsdweb / + +USER 2001:2001 + +CMD ["/fsdweb"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..38dc91e --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,39 @@ +services: + fsd: + build: + context: . + dockerfile: Dockerfile_fsd + restart: unless-stopped + container_name: openfsd_fsd + hostname: openfsd_fsd + expose: + - "13618/tcp" # Internal HTTP REST API service. The webserver talks to this in order to obtain FSD state info. + ports: + - "6809/tcp" + environment: + DATABASE_SOURCE_NAME: /db/openfsd.db?_pragma=busy_timeout(5000)&_pragma=journal_mode(WAL) + DATABASE_AUTO_MIGRATE: true + volumes: + - sqlite:/db + + fsdweb: + build: + context: . + dockerfile: Dockerfile_web + restart: unless-stopped + container_name: openfsd_web + hostname: openfsd_web + expose: + - "8000/tcp" + environment: + DATABASE_SOURCE_NAME: /db/openfsd.db?_pragma=busy_timeout(5000)&_pragma=journal_mode(WAL) + FSD_HTTP_SERVICE_ADDRESS: "http://openfsd_fsd:13618" + volumes: + - sqlite:/db + +networks: + openfsd: + name: openfsd_net + +volumes: + sqlite: diff --git a/fsd/env.go b/fsd/env.go index 124cded..b4befc1 100644 --- a/fsd/env.go +++ b/fsd/env.go @@ -11,7 +11,7 @@ type ServerConfig struct { DatabaseDriver string `env:"DATABASE_DRIVER, default=sqlite"` // Golang sql database driver name DatabaseSourceName string `env:"DATABASE_SOURCE_NAME, default=:memory:"` // Golang sql database source name DatabaseAutoMigrate bool `env:"DATABASE_AUTO_MIGRATE, default=false"` // Whether to automatically run database migrations on startup - DatabaseMaxConns int `env:"DATABASE_MAX_CONNS, default=4"` // Max number of database connections + DatabaseMaxConns int `env:"DATABASE_MAX_CONNS, default=1"` // Max number of database connections NumMetarWorkers int `env:"NUM_METAR_WORKERS, default=4"` // Number of METAR fetch workers to run diff --git a/main.go b/main.go index 06c2902..5e96699 100644 --- a/main.go +++ b/main.go @@ -4,7 +4,6 @@ import ( "context" "github.com/renorris/openfsd/fsd" "log/slog" - _ "modernc.org/sqlite" "os" "os/signal" ) @@ -13,11 +12,6 @@ func main() { setSlogLevel() ctx, _ := signal.NotifyContext(context.Background(), os.Interrupt) - - os.Setenv("DATABASE_AUTO_MIGRATE", "true") - os.Setenv("DATABASE_DRIVER", "sqlite") - os.Setenv("DATABASE_SOURCE_NAME", "test.db") - server, err := fsd.NewDefaultServer(ctx) if err != nil { panic(err) @@ -26,7 +20,7 @@ func main() { if err = server.Run(ctx); err != nil { slog.Error(err.Error()) } - slog.Info("server closed") + slog.Info("FSD server closed") } func setSlogLevel() { diff --git a/web/env.go b/web/env.go index 86dab0a..54545c0 100644 --- a/web/env.go +++ b/web/env.go @@ -8,10 +8,9 @@ import ( type ServerConfig struct { ListenAddr string `env:"LISTEN_ADDR, default=:8000"` // HTTP listen address - DatabaseDriver string `env:"DATABASE_DRIVER, default=sqlite"` // Golang sql database driver name - DatabaseSourceName string `env:"DATABASE_SOURCE_NAME, default=:memory:"` // Golang sql database source name - DatabaseAutoMigrate bool `env:"DATABASE_AUTO_MIGRATE, default=false"` // Whether to automatically run database migrations on startup - DatabaseMaxConns int `env:"DATABASE_MAX_CONNS, default=1"` // Max number of database connections + DatabaseDriver string `env:"DATABASE_DRIVER, default=sqlite"` // Golang sql database driver name + DatabaseSourceName string `env:"DATABASE_SOURCE_NAME, default=:memory:"` // Golang sql database source name + DatabaseMaxConns int `env:"DATABASE_MAX_CONNS, default=1"` // Max number of database connections FsdHttpServiceAddress string `env:"FSD_HTTP_SERVICE_ADDRESS, required"` // HTTP address to talk to the FSD http service } diff --git a/web/main.go b/web/main.go index 63fa28f..b20690f 100644 --- a/web/main.go +++ b/web/main.go @@ -9,10 +9,6 @@ import ( func main() { ctx, _ := signal.NotifyContext(context.Background(), os.Interrupt) - os.Setenv("DATABASE_DRIVER", "sqlite") - os.Setenv("DATABASE_SOURCE_NAME", "../test.db") - os.Setenv("FSD_HTTP_SERVICE_ADDRESS", "http://localhost:13618") - server, err := NewDefaultServer(ctx) if err != nil { panic(err) diff --git a/web/routes.go b/web/routes.go index 1d4e7bf..a71bac4 100644 --- a/web/routes.go +++ b/web/routes.go @@ -1,11 +1,17 @@ package main import ( + "embed" "github.com/gin-gonic/gin" + "io/fs" + "log" "net/http" "os" ) +//go:embed static/* +var staticFS embed.FS + func (s *Server) setupRoutes() (e *gin.Engine) { e = gin.New() e.Use(gin.Recovery()) @@ -29,7 +35,11 @@ func (s *Server) setupRoutes() (e *gin.Engine) { s.setupFrontendRoutes(e.Group("")) // Serve static files - e.Static("/static", "./static") + subFS, err := fs.Sub(staticFS, "static") + if err != nil { + log.Fatal(err) + } + e.StaticFS("/static", http.FS(subFS)) return }