feat: add Docker Compose deployment and serve assets via StaticFS

Embed full assets directory and serve with via's StaticFS instead of a
custom HTTP handler. Move SQLite DB to data/c4.db for clean volume
mounting. Add multi-stage Dockerfile, docker-compose.yml, and
.dockerignore.
This commit is contained in:
Ryan Hamamura
2026-02-13 08:02:15 -10:00
parent b0449fbeb9
commit 427521505b
4 changed files with 70 additions and 11 deletions

10
.dockerignore Normal file
View File

@@ -0,0 +1,10 @@
c4
c4.db
data/
deploy/
.env
.git
.gitignore
assets/css/output.css
c4-deploy-*.tar.gz
c4-deploy-*_b64*.txt

29
Dockerfile Normal file
View File

@@ -0,0 +1,29 @@
FROM golang:1.25.4-bookworm AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN go tool gotailwind -i assets/css/input.css -o assets/css/output.css --minify
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o /c4 .
FROM debian:bookworm-slim
RUN apt-get update && \
apt-get install -y --no-install-recommends ca-certificates wget && \
rm -rf /var/lib/apt/lists/*
COPY --from=build /c4 /usr/local/bin/c4
WORKDIR /app
RUN mkdir data && chown games:games data
USER games
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \
CMD wget -qO /dev/null http://localhost:8080/
CMD ["c4"]

17
docker-compose.yml Normal file
View File

@@ -0,0 +1,17 @@
services:
c4:
build: .
container_name: c4
restart: unless-stopped
ports:
- "8080:8080"
env_file:
- path: .env
required: false
environment:
- PORT=8080
volumes:
- c4-data:/app/data
volumes:
c4-data:

25
main.go
View File

@@ -4,11 +4,11 @@ import (
"context"
"crypto/md5"
"database/sql"
_ "embed"
"embed"
"encoding/hex"
"encoding/json"
"io/fs"
"log"
"net/http"
"os"
"sync"
"time"
@@ -32,17 +32,14 @@ var (
queries *gen.Queries
)
//go:embed assets/css/output.css
var daisyUICSS []byte
//go:embed assets
var assets embed.FS
func DaisyUIPlugin(v *via.V) {
sum := md5.Sum(daisyUICSS)
css, _ := fs.ReadFile(assets, "assets/css/output.css")
sum := md5.Sum(css)
version := hex.EncodeToString(sum[:4])
v.HTTPServeMux().HandleFunc("GET /_plugins/daisyui/style.css", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/css")
_, _ = w.Write(daisyUICSS)
})
v.AppendToHead(h.Link(h.Rel("stylesheet"), h.Href("/_plugins/daisyui/style.css?v="+version)))
v.AppendToHead(h.Link(h.Rel("stylesheet"), h.Href("/assets/css/output.css?v="+version)))
}
func port() string {
@@ -55,7 +52,10 @@ func port() string {
func main() {
_ = godotenv.Load()
if err := db.Init("c4.db"); err != nil {
if err := os.MkdirAll("data", 0o755); err != nil {
log.Fatal(err)
}
if err := db.Init("data/c4.db"); err != nil {
log.Fatal(err)
}
queries = gen.New(db.DB)
@@ -76,6 +76,9 @@ func main() {
Plugins: []via.Plugin{DaisyUIPlugin},
})
subFS, _ := fs.Sub(assets, "assets")
v.StaticFS("/assets/", subFS)
store.SetPubSub(v.PubSub())
snakeStore.SetPubSub(v.PubSub())