diff --git a/Dockerfile b/Dockerfile index 851b925..74d5a1b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,70 +1,30 @@ -# Multi-stage build for Aethera -# Stage 1: Build frontend assets +# Step 1: Build Frontend FROM oven/bun:1 AS frontend-builder - WORKDIR /app/frontend - -# Copy frontend package files COPY frontend/package.json frontend/bun.lock ./ - -# Install dependencies RUN bun install --frozen-lockfile - -# Copy frontend source code COPY frontend/ ./ - -# Build frontend assets RUN bun run build -# Stage 2: Build Go binary +# Stage 2: Build Backend FROM golang:1.25-alpine AS backend-builder - WORKDIR /app - -# Install build dependencies RUN apk add --no-cache git - -# Copy go mod files COPY backend/go.mod backend/go.sum ./ - -# Download Go dependencies RUN go mod download - -# Copy backend source code COPY backend/ ./ +COPY --from=frontend-builder /app/frontend/public/ ./web/static/ +RUN go build -ldflags="-w -s" -o aethera ./cmd -# Copy frontend assets from previous stage -COPY --from=frontend-builder /app/frontend/public/dist ./public/dist -COPY --from=frontend-builder /app/frontend/public/index.html ./public/ -COPY --from=frontend-builder /app/frontend/public/pages ./public/pages - -# Build the Go binary -RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-w -s" -o aethera ./cmd - -# Stage 3: Create minimal runtime image +# Stage 3: Minimal Runtime FROM alpine:3.21 - -# Install ca-certificates for HTTPS calls RUN apk add --no-cache ca-certificates - WORKDIR /app - -# Copy the binary from the builder stage COPY --from=backend-builder /app/aethera . - -# Copy static assets -COPY --from=backend-builder /app/public ./public - -# Create data directory RUN mkdir -p /app/data -# Expose the default port EXPOSE 8080 - -# Set environment variable defaults ENV AETHERA_LISTEN=0.0.0.0 ENV AETHERA_PORT=8080 ENV AETHERA_DATA_DIR=/app/data - -# Set the entrypoint ENTRYPOINT ["./aethera"] diff --git a/Makefile b/Makefile index fb4861a..6d7317c 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,9 @@ all: frontend backend frontend: cd frontend && bun run build + mkdir -p backend/web/static + cp frontend/public/index.html backend/web/static/ 2>/dev/null || true + cp -r frontend/public/pages backend/web/static/ 2>/dev/null || true backend: cd backend && go build -o ./dist/aethera ./cmd @@ -11,6 +14,7 @@ backend: clean: rm -rf frontend/public/dist rm -rf backend/dist + rm -rf backend/web/static dev: cd backend && go run ./cmd --listen 0.0.0.0 & diff --git a/backend/.gitignore b/backend/.gitignore index 1521c8b..0d2c0af 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -1 +1,2 @@ dist +web/static/ diff --git a/backend/internal/server/server.go b/backend/internal/server/server.go index 3c948ca..9c0c9de 100644 --- a/backend/internal/server/server.go +++ b/backend/internal/server/server.go @@ -2,6 +2,7 @@ package server import ( "fmt" + "io/fs" "net/http" "path" "time" @@ -9,6 +10,7 @@ import ( "github.com/sirupsen/logrus" "reichard.io/aethera/internal/api" "reichard.io/aethera/internal/store" + "reichard.io/aethera/web" ) func StartServer(settingsStore store.Store, dataDir, listenAddress string, listenPort int) { @@ -17,12 +19,13 @@ func StartServer(settingsStore store.Store, dataDir, listenAddress string, liste // Create API Instance - use settingsStore as the unified store for both settings and chat logger := logrus.New() api := api.New(settingsStore, dataDir, logger) - feFS := http.FileServer(http.Dir("../frontend/public/")) - mux.Handle("GET /", feFS) - // Serve UI Pages - pagesFS := http.FileServer(http.Dir("../frontend/public/pages/")) - mux.Handle("GET /pages/", http.StripPrefix("/pages/", pagesFS)) + // Serve embedded static assets + staticFS, err := fs.Sub(web.Assets, "static") + if err != nil { + logrus.Fatal("Failed to create static filesystem: ", err) + } + mux.Handle("GET /", http.FileServer(http.FS(staticFS))) // Serve Generated Data genFS := http.FileServer(http.Dir(path.Join(dataDir, "generated"))) diff --git a/backend/web/embed.go b/backend/web/embed.go new file mode 100644 index 0000000..862f937 --- /dev/null +++ b/backend/web/embed.go @@ -0,0 +1,6 @@ +package web + +import "embed" + +//go:embed static/* +var Assets embed.FS diff --git a/flake.nix b/flake.nix index 5950761..41714d3 100644 --- a/flake.nix +++ b/flake.nix @@ -21,11 +21,6 @@ config.allowUnfree = true; } ); - - oc = pkgs.writeShellScriptBin "oc" '' - PRJ_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) - cd "$PRJ_ROOT" && OPENCODE_EXPERIMENTAL_LSP_TOOL=true opencode - ''; in { devShells.default = pkgs.mkShell { @@ -38,11 +33,11 @@ # Frontend bun watchman - tailwindcss_4 - - # Custom Commands - oc ]; + + shellHook = '' + export LD_LIBRARY_PATH=${pkgs.stdenv.cc.cc.lib}/lib:$LD_LIBRARY_PATH + ''; }; } ); diff --git a/frontend/package.json b/frontend/package.json index d293dae..f262041 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -2,8 +2,8 @@ "name": "aethera", "private": true, "scripts": { - "dev": "bun build src/main.ts --outdir public/dist --target browser --watch & bunx @tailwindcss/cli -i styles.css -o public/dist/styles.css --watch", - "build": "bun build src/main.ts --outdir public/dist --target browser && bunx @tailwindcss/cli -i styles.css -o public/dist/styles.css --minify", + "dev": "bun build src/main.ts --outdir public/dist --target browser --watch & bunx tailwindcss -i styles.css -o public/dist/styles.css --watch", + "build": "bun build src/main.ts --outdir ../backend/web/static/dist --target browser && bunx tailwindcss -i styles.css -o ../backend/web/static/dist/styles.css --minify", "lint": "eslint ./src/**" }, "devDependencies": {