diff --git a/deploy/docker/Dockerfile.server b/deploy/docker/Dockerfile.server index eaa4fee..06a161e 100644 --- a/deploy/docker/Dockerfile.server +++ b/deploy/docker/Dockerfile.server @@ -1,11 +1,16 @@ -# BetterFrame server image — Node 24 + native deps for argon2/sqlite. -# Trixie base (Debian 13, latest stable) — matches the host distro we -# recommend in deploy/README.md. +# BetterFrame server — BSB container with built plugins. +# +# sec-config.yaml is NOT baked in — mount it at runtime: +# volumes: +# - ./sec-config.yaml:/app/sec-config.yaml:ro +# +# Builder stage compiles TS + native deps (argon2). +# Runtime stage uses the official BSB container. + FROM node:24-trixie-slim AS builder WORKDIR /app -# Build deps for argon2 + bsb-plugin-cli RUN apt-get update && apt-get install -y --no-install-recommends \ build-essential python3 \ && rm -rf /var/lib/apt/lists/* @@ -17,48 +22,40 @@ RUN npm ci && npm rebuild argon2 COPY server ./server -# Run BSB build — extracts schemas + compiles TS + generates plugin manifests WORKDIR /app/server RUN npm run build -# ---- Runtime image ---- -FROM node:24-trixie-slim +# ---- Runtime ---- +FROM betterweb/service-base:node -# Version baked at build time. Coolify doesn't include .git in build context -# so we can't derive from git inside Docker. Instead, compose passes it as a -# build arg from SOURCE_COMMIT / COOLIFY_GIT_COMMIT env vars that Coolify -# DOES inject into the build environment. ARG BF_SERVER_VERSION=dev -# ffmpeg for camera snapshot capture (optional but needed for /admin/entities/:id/snapshot) +USER root + +# ffmpeg for camera snapshot capture RUN apt-get update && apt-get install -y --no-install-recommends \ - ca-certificates ffmpeg wget \ + ffmpeg \ && rm -rf /var/lib/apt/lists/* -RUN useradd -m -d /var/lib/betterframe -s /bin/false betterframe +RUN mkdir -p /var/lib/betterframe && chown node:node /var/lib/betterframe WORKDIR /app +# Copy built plugin + deps COPY --from=builder /app/node_modules ./node_modules -COPY --from=builder /app/server ./server -COPY --from=builder /app/tsconfig.base.json ./ -COPY --from=builder /app/package.json ./ +COPY --from=builder /app/server/lib ./lib +COPY --from=builder /app/server/bsb-plugin.json ./bsb-plugin.json +COPY --from=builder /app/server/package.json ./package.json +COPY --from=builder /app/tsconfig.base.json ./tsconfig.base.json -# Default sec-config baked into image. BF_* env vars in compose override at -# runtime (see shared/env-overrides.ts). No host bind mount needed. -COPY deploy/docker/sec-config.yaml /app/server/sec-config.yaml +# Static web assets served by admin-http +COPY --from=builder /app/server/lib/web-static ./lib/web-static -# Bake version into a file readable by version.ts at runtime. -RUN echo "$BF_SERVER_VERSION" > /app/server/.bf-version +# Bake version +RUN echo "$BF_SERVER_VERSION" > /app/.bf-version -RUN mkdir -p /var/lib/betterframe && chown betterframe:betterframe /var/lib/betterframe VOLUME /var/lib/betterframe EXPOSE 18080 18081 18082 -USER betterframe -WORKDIR /app/server - -ENV NODE_OPTIONS=--import=tsx - -CMD ["node", "--import", "tsx", "/app/node_modules/@bsb/base/lib/scripts/bsb-plugin-cli.js", "start"] +USER node diff --git a/deploy/docker/sec-config.yaml b/deploy/docker/sec-config.yaml deleted file mode 100644 index 69de18e..0000000 --- a/deploy/docker/sec-config.yaml +++ /dev/null @@ -1,65 +0,0 @@ -# BSB runtime configuration for the Docker compose stack. -# Backend services bind all interfaces inside the private compose network; -# Angie/nginx is the only published host port. - -default: - observable: - observable-default: - plugin: observable-default - enabled: true - config: {} - events: - events-default: - plugin: events-default - enabled: true - services: - service-store: - plugin: service-store - enabled: true - config: - sqlitePath: /var/lib/betterframe/betterframe.db - - service-admin-http: - plugin: service-admin-http - enabled: true - config: - host: 0.0.0.0 - port: 18080 - dataDir: /var/lib/betterframe - sessionIdleSeconds: 43200 - sessionMaxSeconds: 2592000 - loginLockoutThreshold: 8 - loginLockoutSeconds: 900 - argon2Memory: 65536 - argon2TimeCost: 3 - argon2Parallelism: 2 - cookieName: betterframe_session - totpIssuer: BetterFrame - noderedUrl: http://nodered:1880 - selfUrl: http://server:18080 - - service-api-http: - plugin: service-api-http - enabled: true - config: - host: 0.0.0.0 - port: 18081 - codeTtlSeconds: 600 - dataDir: /var/lib/betterframe - argon2Memory: 65536 - argon2TimeCost: 3 - argon2Parallelism: 2 - noderedUrl: http://nodered:1880 - - service-coordinator-ws: - plugin: service-coordinator-ws - enabled: true - config: - host: 0.0.0.0 - port: 18082 - noderedUrl: http://nodered:1880 - dataDir: /var/lib/betterframe - cookieName: betterframe_session - argon2Memory: 65536 - argon2TimeCost: 3 - argon2Parallelism: 2 diff --git a/docker-compose.coolify.yml b/docker-compose.coolify.yml index 1864bbc..899b87b 100644 --- a/docker-compose.coolify.yml +++ b/docker-compose.coolify.yml @@ -25,6 +25,7 @@ services: - TZ=UTC volumes: - betterframe-data:/var/lib/betterframe + - ./sec-config.yaml:/app/sec-config.yaml:ro expose: - "18080" - "18081" diff --git a/docker-compose.yml b/docker-compose.yml index 8f3276e..0f0d78d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -31,29 +31,11 @@ services: BF_SERVER_VERSION: ${BF_SERVER_VERSION:-} container_name: betterframe-server restart: unless-stopped - # Env overrides win over sec-config.yaml — Coolify / k8s inject these. environment: - TZ=UTC - - BF_DATA_DIR=/var/lib/betterframe - - BF_SQLITE_PATH=/var/lib/betterframe/betterframe.db - - BF_NODERED_URL=http://nodered:1880 - - BF_SELF_URL=http://server:18080 - - BF_SERVER_VERSION=${BF_SERVER_VERSION:-} - - COOLIFY_GIT_COMMIT=${COOLIFY_GIT_COMMIT:-} - - SOURCE_COMMIT=${SOURCE_COMMIT:-} - # Optional: paste Ed25519 PEM private key here for firmware signing. - # - BF_FIRMWARE_SIGNING_KEY= - # Optional: single-purpose Bearer token for GitHub Actions firmware import. - # Set GitHub's BF_AUTOIMPORT_API_KEY secret to the same value. - # Generate with: openssl rand -base64 32 - # - BF_FIRMWARE_IMPORT_API_KEY= - # Optional MQTT telemetry bridge (ThingsBoard / HA / Influx / etc). - # - BF_MQTT_URL=mqtt://broker:1883 - # - BF_MQTT_USERNAME= - # - BF_MQTT_PASSWORD= - # - BF_MQTT_TOPIC_PREFIX=betterframe volumes: - betterframe-data:/var/lib/betterframe + - ./sec-config.yaml:/app/sec-config.yaml:ro expose: - "18080" - "18081" diff --git a/server/src/shared/version.ts b/server/src/shared/version.ts index 41f7a77..ee22afb 100644 --- a/server/src/shared/version.ts +++ b/server/src/shared/version.ts @@ -5,7 +5,7 @@ let cached: string | null = null; export function serverVersion(): string { if (cached) return cached; try { - const v = readFileSync("/app/server/.bf-version", "utf8").trim(); + const v = readFileSync("/app/.bf-version", "utf8").trim(); cached = v && v !== "dev" ? v : "dev"; } catch { cached = "dev";