-
Notifications
You must be signed in to change notification settings - Fork 43
Expand file tree
/
Copy pathdocker-compose.yml
More file actions
193 lines (187 loc) · 7.81 KB
/
Copy pathdocker-compose.yml
File metadata and controls
193 lines (187 loc) · 7.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# openflipbook — one-command local stack.
#
# cp .env.example .env # drop in FAL_KEY (+ OPENROUTER_API_KEY)
# docker compose up --build # → http://localhost:3000/play
#
# Stores run fully local: Mongo for metadata, Minio (S3-compatible) for image
# blobs — so no Cloudflare R2 / hosted Mongo to provision. The AI still calls
# out (OpenRouter for the planner/VLM, fal for images). For a no-cloud LLM too,
# add the Ollama override: make demo-local (see docker-compose.local.yml).
#
# Overridable defaults are read from `.env` (compose auto-loads it); the values
# below are the local-stack fallbacks, so a bare `docker compose up` just works.
services:
mongo:
image: mongo:7
container_name: openflipbook-mongo
restart: unless-stopped
# Single-node replica set (Wave 8): change streams power the shared-
# session read-along, and Mongo only serves them from a replica set.
# Existing standalone data volumes convert in place — mongo-init runs a
# one-time rs.initiate() pinned to the compose hostname. Clients OUTSIDE
# the compose network (local `pnpm dev` against localhost:27017) need
# `?directConnection=true` on their MONGODB_URI, since the set advertises
# `mongo:27017`.
command: ["--replSet", "rs0", "--bind_ip_all", "--quiet"]
ports:
- "27017:27017"
volumes:
- mongo-data:/data/db
healthcheck:
test: ["CMD", "mongosh", "--quiet", "--eval", "db.adminCommand('ping')"]
interval: 10s
timeout: 5s
retries: 5
mongo-init:
image: mongo:7
container_name: openflipbook-mongo-init
restart: "no"
depends_on:
mongo:
condition: service_healthy
# Idempotent: initiates the set on first boot, no-ops once initiated.
command: >
mongosh --host mongo --quiet --eval
"try { rs.status() } catch (e) { rs.initiate({_id: 'rs0', members: [{_id: 0, host: 'mongo:27017'}]}) }"
# S3-compatible blob store — the local stand-in for Cloudflare R2. The web
# app writes here (R2_ENDPOINT=http://minio:9000) and the browser reads image
# URLs from the published port (R2_PUBLIC_BASE_URL=http://localhost:9000/...).
minio:
image: minio/minio:latest
container_name: openflipbook-minio
restart: unless-stopped
command: server /data --console-address ":9001"
environment:
MINIO_ROOT_USER: ${MINIO_ROOT_USER:-openflipbook}
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-openflipbook-local}
ports:
- "9000:9000" # S3 API (also the browser's image origin)
- "9001:9001" # web console
volumes:
- minio-data:/data
# One-shot: create the bucket and make it anonymously readable, so the
# browser's <img src> GETs aren't 403'd. Retries until Minio answers, then
# exits — `web` waits on it completing.
minio-setup:
image: minio/mc:latest
container_name: openflipbook-minio-setup
depends_on:
- minio
environment:
MINIO_ROOT_USER: ${MINIO_ROOT_USER:-openflipbook}
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-openflipbook-local}
R2_BUCKET: ${R2_BUCKET:-openflipbook}
entrypoint: |
/bin/sh -c '
until mc alias set local http://minio:9000 "$${MINIO_ROOT_USER}" "$${MINIO_ROOT_PASSWORD}"; do
echo "waiting for minio..."; sleep 1;
done;
mc mb --ignore-existing "local/$${R2_BUCKET}";
mc anonymous set download "local/$${R2_BUCKET}";
echo "minio bucket $${R2_BUCKET} ready";
'
restart: "no"
backend:
build:
context: .
dockerfile: apps/modal-backend/Dockerfile
container_name: openflipbook-backend
restart: unless-stopped
# Keys come from the root .env (single-file demo) or, for power users, the
# backend's own .env which overrides it. Both optional; neither clobbers
# unset keys.
env_file:
- path: .env
required: false
- path: apps/modal-backend/.env
required: false
environment:
# Weighted multi-reference image conditioning (default on). Set to false
# to A/B against the old text-only generation:
# IMAGE_CONDITIONING=false docker compose up -d backend
IMAGE_CONDITIONING: ${IMAGE_CONDITIONING:-true}
# Zero-key demo mode: deterministic mock providers (no fal/OpenRouter
# calls, no keys needed). MOCK_PROVIDERS=1 docker compose up -d
MOCK_PROVIDERS: ${MOCK_PROVIDERS:-}
# World Mode: a tap ENTERS the tapped place (an immersive scene / a closer
# sub-map) instead of explaining a topic, and places persist + reopen.
# Default off — turn on per-session from the UI once enabled here:
# WORLD_MODE=true docker compose up -d backend
WORLD_MODE: ${WORLD_MODE:-false}
# Map-pan: "expand" (Around) outpaints the world outward in 4 directions
# instead of blooming neighbour subjects. Default off:
# EXPAND_MAP_PAN=true docker compose up -d backend
EXPAND_MAP_PAN: ${EXPAND_MAP_PAN:-false}
# Force public resolvers — Docker's default 127.0.0.11 link-local resolver
# has been flaking on the fal.ai / r2.cloudflarestorage.com hosts during
# E2E runs (sporadic ESERVFAIL / EAI_AGAIN), which manifests as
# ConnectError mid-stream and tanks the click-to-next + style-pin tests.
dns:
- 1.1.1.1
- 8.8.8.8
ports:
- "8787:8787"
healthcheck:
test: ["CMD", "curl", "-fsS", "http://127.0.0.1:8787/health"]
interval: 15s
timeout: 5s
retries: 3
web:
build:
context: .
dockerfile: apps/web/Dockerfile
# NEXT_PUBLIC_* are inlined at BUILD time, so runtime env_file can't
# carry them — they must arrive as build args or the image ships the
# flag-off bundle (the "why did consistency regress" footgun: world
# mode seeded OFF in every fresh docker session). Both follow the root
# .env's WORLD_MODE so the single-file demo stays single-file.
args:
NEXT_PUBLIC_WORLD_MODE: ${NEXT_PUBLIC_WORLD_MODE:-${WORLD_MODE:-false}}
NEXT_PUBLIC_DOM_LABELS: ${NEXT_PUBLIC_DOM_LABELS:-${WORLD_MODE:-false}}
NEXT_PUBLIC_ON_RAMP_COACH: ${NEXT_PUBLIC_ON_RAMP_COACH:-false}
NEXT_PUBLIC_ENTER_COACH: ${NEXT_PUBLIC_ENTER_COACH:-false}
container_name: openflipbook-web
restart: unless-stopped
# Same posture as the backend: the root .env (single-file demo) carries
# the world/geo feature flags — the web's server routes gate geometry
# seeding (GEOMETRIC_WORLD) and scale-nav on them, so without this the
# docker stack silently ran with seeding off while local dev had it on.
# apps/web/.env.local overrides; the compose-network values below win last.
env_file:
- path: .env
required: false
- path: apps/web/.env.local
required: false
dns:
- 1.1.1.1
- 8.8.8.8
environment:
NODE_ENV: production
HOSTNAME: 0.0.0.0
PORT: "3000"
# Backend URL is fixed inside the compose network.
MODAL_API_URL: http://backend:8787
# Metadata → local Mongo by default.
MONGODB_URI: ${MONGODB_URI:-mongodb://mongo:27017/?directConnection=true}
MONGODB_DB: ${MONGODB_DB:-openflipbook}
# Blobs → local Minio by default. R2_ENDPOINT makes the S3 client target
# Minio (path-style); the public base URL is what the browser fetches.
R2_ENDPOINT: ${R2_ENDPOINT:-http://minio:9000}
R2_BUCKET: ${R2_BUCKET:-openflipbook}
R2_ACCESS_KEY_ID: ${MINIO_ROOT_USER:-openflipbook}
R2_SECRET_ACCESS_KEY: ${MINIO_ROOT_PASSWORD:-openflipbook-local}
R2_PUBLIC_BASE_URL: ${R2_PUBLIC_BASE_URL:-http://localhost:9000/openflipbook}
ports:
- "3000:3000"
depends_on:
backend:
condition: service_healthy
mongo:
condition: service_healthy
mongo-init:
condition: service_completed_successfully
minio-setup:
condition: service_completed_successfully
volumes:
mongo-data:
minio-data: