Skip to content

Commit 916697a

Browse files
kelsi-bizerclaude
andauthored
Phase 3: replace docker image with notes-app + file-api bundle (#8)
The image now ships the post-Logseq stack: notes-app SPA served by nginx, file-api sidecar on 127.0.0.1:3000, /brain mount for markdown persistence. Pulls back from the upstream-Logseq build that the image was inheriting. Multi-stage Dockerfile: - notes-app-builder: pnpm install + vite build -> dist - file-api-builder: pnpm install --prod -> node_modules + src - runtime (node:22-alpine + nginx + tini): SPA at /usr/share/nginx/html, file-api at /opt/file-api, started by /entrypoint.sh docker/nginx.conf: - /api/ proxied to 127.0.0.1:3000 with HTTP/1.1, no buffering, and 24h read timeout so /api/watch SSE streams stay alive - SPA fallback: try_files $uri $uri/ /index.html for hash routing - client_max_body_size 25M to match file-api's MAX_BODY_BYTES default docker/entrypoint.sh: - mkdir -p $BRAIN_DIR (default /brain) - start file-api in background; trap TERM/INT to forward shutdown - poll /api/health for up to 5s before starting nginx so the first request doesn't 502 - exec nginx in foreground so PID 1 (tini) reaps it correctly .dockerignore: scope context to docker/ + packages/notes-app + packages/file-api; exclude all upstream-Logseq dirs and node_modules trees. Build context is ~1.5 MB. .gitignore: drop the bare 'docker' rule inherited from upstream so the new docker/ directory at the repo root is tracked. HEALTHCHECK: hit /api/health every 30s. VOLUME ["/brain"], EXPOSE 80. Run with: docker run -v /host/brain:/brain -p 8080:80 \ ghcr.io/kelsi-bizer/bizeros-knowledge:latest Co-authored-by: Claude <noreply@anthropic.com>
1 parent a5c3672 commit 916697a

5 files changed

Lines changed: 140 additions & 32 deletions

File tree

.dockerignore

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,35 @@
1+
# Keep the docker build context lean. Only packages/notes-app, packages/file-api,
2+
# and docker/ are needed to build the BizerOS Knowledge image.
3+
14
.git
25
.github
3-
node_modules
6+
*.log
7+
.DS_Store
8+
9+
# Build outputs and dependency dirs that should never be sent.
410
**/node_modules
5-
static
6-
public
7-
dist
8-
ios
11+
**/dist
12+
**/.parcel-cache
13+
**/.vite
14+
**/*.tsbuildinfo
15+
16+
# Upstream-Logseq directories the new image does not consume.
917
android
18+
ios
1019
fastlane
1120
docs
21+
src
22+
deps
23+
scripts
24+
icons
25+
resources
26+
public
27+
static
28+
prompts
1229
clj-e2e
1330
cli-e2e
14-
*.log
15-
.DS_Store
31+
externs
32+
externs.js
33+
ssl
34+
libs
35+
bin

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ charlie/
4747
.vscode
4848
/.claude
4949
/.preprocessor-cljs
50-
docker
5150
android/app/src/main/assets/capacitor.plugin.json
5251
ios/App/App/capacitor.config.json
5352
ios/App/App/public

Dockerfile

Lines changed: 41 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,51 @@
1-
# Builds the BizerOS Knowledge web app image from this repository's source.
2-
# The build context is the repo root, so the rebranded sources are baked into
3-
# the resulting image rather than being re-cloned from upstream Logseq.
1+
# BizerOS Knowledge — multi-stage build.
2+
# Final image runs nginx (serving the notes-app SPA) and a file-api sidecar
3+
# (persisting markdown to $BRAIN_DIR, default /brain).
44

5-
# Builder image
6-
FROM clojure:temurin-11-tools-deps-1.11.1.1208-bullseye-slim as builder
5+
# ── Stage 1: build the notes-app SPA ─────────────────────────────────────────
6+
FROM node:22-alpine AS notes-app-builder
7+
WORKDIR /build
8+
RUN corepack enable && corepack prepare pnpm@10.33.0 --activate
79

8-
ARG DEBIAN_FRONTEND=noninteractive
10+
COPY packages/notes-app/package.json packages/notes-app/pnpm-lock.yaml ./
11+
RUN pnpm install --frozen-lockfile
912

10-
# Install reqs
11-
RUN apt-get update && apt-get install -y --no-install-recommends \
12-
curl \
13-
ca-certificates \
14-
apt-transport-https \
15-
gpg \
16-
build-essential libcairo2-dev libpango1.0-dev libjpeg-dev libgif-dev librsvg2-dev
13+
COPY packages/notes-app/ ./
14+
RUN pnpm build
1715

18-
# install NodeJS & pnpm
19-
RUN curl -fsSL https://deb.nodesource.com/setup_24.x | bash - && \
20-
apt-get update && apt-get install -y nodejs && \
21-
corepack enable && corepack prepare pnpm@10.33.0 --activate
16+
# ── Stage 2: install the file-api runtime ────────────────────────────────────
17+
FROM node:22-alpine AS file-api-builder
18+
WORKDIR /build
19+
RUN corepack enable && corepack prepare pnpm@10.33.0 --activate
2220

23-
WORKDIR /data
21+
COPY packages/file-api/package.json packages/file-api/pnpm-lock.yaml ./
22+
RUN pnpm install --frozen-lockfile --prod
23+
COPY packages/file-api/src ./src
2424

25-
COPY . /data
25+
# ── Stage 3: runtime ─────────────────────────────────────────────────────────
26+
FROM node:22-alpine
2627

27-
RUN pnpm install --config.network-timeout=240000
28+
# nginx is the front door; tini reaps zombies and forwards signals.
29+
RUN apk add --no-cache nginx tini
2830

29-
RUN pnpm release
31+
# SPA bundle.
32+
COPY --from=notes-app-builder /build/dist /usr/share/nginx/html
3033

31-
# Web App Runner image
32-
FROM nginx:1.24.0-alpine3.17
34+
# File-api source + node_modules.
35+
COPY --from=file-api-builder /build /opt/file-api
3336

34-
COPY --from=builder /data/static /usr/share/nginx/html
37+
# nginx + entrypoint configuration.
38+
COPY docker/nginx.conf /etc/nginx/http.d/default.conf
39+
COPY docker/entrypoint.sh /entrypoint.sh
40+
RUN chmod +x /entrypoint.sh
41+
42+
# /brain is the markdown persistence root. Mount a host volume here.
43+
ENV BRAIN_DIR=/brain
44+
VOLUME ["/brain"]
45+
46+
EXPOSE 80
47+
48+
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
49+
CMD wget --quiet --tries=1 --spider http://127.0.0.1/api/health || exit 1
50+
51+
ENTRYPOINT ["/sbin/tini", "--", "/entrypoint.sh"]

docker/entrypoint.sh

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#!/bin/sh
2+
# BizerOS Knowledge container entrypoint.
3+
# Runs the file-api sidecar in the background and nginx in the foreground.
4+
5+
set -eu
6+
7+
BRAIN_DIR="${BRAIN_DIR:-/brain}"
8+
mkdir -p "$BRAIN_DIR"
9+
10+
export BRAIN_DIR
11+
export HOST="${HOST:-127.0.0.1}"
12+
export PORT="${PORT:-3000}"
13+
export LOG_LEVEL="${LOG_LEVEL:-info}"
14+
15+
echo "[entrypoint] BRAIN_DIR=$BRAIN_DIR" >&2
16+
echo "[entrypoint] starting file-api on ${HOST}:${PORT}" >&2
17+
18+
node /opt/file-api/src/server.js &
19+
FILE_API_PID=$!
20+
21+
# Forward shutdown signals to the file-api before exiting.
22+
trap 'kill -TERM "$FILE_API_PID" 2>/dev/null || true; wait "$FILE_API_PID" 2>/dev/null || true; exit 0' TERM INT
23+
24+
# Wait briefly for the file-api to be ready so nginx doesn't 502 on first hit.
25+
i=0
26+
while [ "$i" -lt 20 ]; do
27+
if wget -q --spider "http://${HOST}:${PORT}/api/health" 2>/dev/null; then
28+
echo "[entrypoint] file-api ready" >&2
29+
break
30+
fi
31+
i=$((i + 1))
32+
sleep 0.25
33+
done
34+
35+
if [ "$i" -ge 20 ]; then
36+
echo "[entrypoint] WARNING: file-api did not respond within 5s — nginx will start anyway" >&2
37+
fi
38+
39+
echo "[entrypoint] starting nginx" >&2
40+
exec nginx -g 'daemon off;'

docker/nginx.conf

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
server {
2+
listen 80;
3+
listen [::]:80;
4+
server_name _;
5+
6+
root /usr/share/nginx/html;
7+
index index.html;
8+
9+
# Match file-api's default MAX_BODY_BYTES (25 MB).
10+
client_max_body_size 25M;
11+
12+
# /api/* is served by the file-api sidecar on 127.0.0.1:3000.
13+
# SSE-friendly: HTTP/1.1, no buffering, long read timeout.
14+
location /api/ {
15+
proxy_pass http://127.0.0.1:3000;
16+
proxy_http_version 1.1;
17+
proxy_set_header Host $host;
18+
proxy_set_header X-Real-IP $remote_addr;
19+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
20+
21+
proxy_buffering off;
22+
proxy_cache off;
23+
proxy_set_header Connection '';
24+
proxy_read_timeout 24h;
25+
proxy_send_timeout 24h;
26+
}
27+
28+
# Hash-routed SPA: any unknown path falls back to index.html.
29+
location / {
30+
try_files $uri $uri/ /index.html;
31+
}
32+
}

0 commit comments

Comments
 (0)