Repository navigation
Expand file tree
/
Copy pathdocker-compose.yml
More file actions
161 lines (156 loc) · 6.85 KB
/
Copy pathdocker-compose.yml
File metadata and controls
161 lines (156 loc) · 6.85 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
services:
# Primary datastore. The app is a multi-writer pipeline; Postgres (MVCC +
# row-level locking) is required, not SQLite. Dev creds only; prod overrides
# via env. Host port 5432 is exposed so host-run tests / psql can reach it.
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: openmagpie
POSTGRES_USER: openmagpie
POSTGRES_PASSWORD: openmagpie
ports:
- "5432:5432"
volumes:
- magpie_pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U openmagpie -d openmagpie"]
interval: 5s
timeout: 3s
retries: 10
start_period: 10s
# One-shot pre-start setup for core, run once per `up` and gated before core
# serves (core depends_on it: service_completed_successfully). Today it just
# ensures the DB-backed cache table (openmagpie_cache) that /healthz round-
# trips; it's the home for anything else that must exist before core serves.
# Kept OUT of core's command so rolling/restarting core alone doesn't re-run
# it, and OUT of migrations: createcachetable is idempotent and NOT a schema
# migration, so it's safe on every `up` without auto-applying model changes
# the dev isn't ready for (those stay explicit: `make local-migrate` / the
# quickstart). Local compose only; assumes DatabaseCache, the local backend.
# Defines the shared build + image (YAML-anchored) that core reuses, so the two
# can't silently drift apart and resolve to one image (Compose builds it once,
# or on older versions rebuilds it fully cached).
core-setup:
# Build context = repo root: the uv workspace's single lock + every member
# pyproject live there; Dockerfile is the core member's.
build: &core_build
context: .
dockerfile: apps/core/Dockerfile
image: &core_image openmagpie-core
command: uv run --package openmagpie-core python apps/core/manage.py createcachetable
env_file: ./apps/core/.env
depends_on:
db:
condition: service_healthy
volumes:
- .:/app
- /app/.venv
core:
# Reuse the core-setup build + image (the anchors above) so the image is
# built once. Pre-start setup lives in core-setup, not in this command, so
# restarting/rolling core alone doesn't re-run it.
build: *core_build
image: *core_image
command: uv run --package openmagpie-core python apps/core/manage.py runserver 0.0.0.0:8000
ports:
- "8000:8000"
env_file: ./apps/core/.env
# Reach a BYO LLM running on the host (the default ENGINE_BASE_URL uses
# host.docker.internal). Docker Desktop (Mac/Win) maps this automatically;
# Linux does NOT, so map it explicitly or the semantic filter can't call the
# host's LLM. Harmless when ENGINE_BASE_URL points elsewhere (remote/hosted).
extra_hosts:
- "host.docker.internal:host-gateway"
depends_on:
# core-setup creates the DB-backed cache table /healthz round-trips, so
# core can't go healthy (and `up --wait` deadlocks) until it completes.
# Waiting on it also means Postgres is up by the time core starts (it
# gates on db: service_healthy in turn).
core-setup:
condition: service_completed_successfully
volumes:
# Bind the whole workspace for hot-reload (canonical uv pattern);
# anonymous volume keeps the root .venv the image built.
- .:/app
- /app/.venv
# Probes /healthz via stdlib urllib (no wget/curl in the image).
# urlopen raises HTTPError on 503 so a degraded DB/cache fails the
# check just as cleanly as the server being down.
healthcheck:
test:
- CMD
- python
- -c
- "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/healthz', timeout=2)"
interval: 5s
timeout: 3s
retries: 6
start_period: 30s
# Sidecar that bypasses anti-bot JS challenges (Cloudflare, Imperva,
# DDoS-Guard, ...) by running headless Chrome in its own process and
# exposing an HTTP API. Any connector mixing in ChallengeBypassMixin
# falls back to this when its normal fetch hits a challenge page.
# Optional ; if the container isn't running, the connector gracefully
# logs and skips the challenge-protected source as before. See
# `SOURCE_CHALLENGE_BYPASS_URL` in core settings.
flaresolverr:
image: ghcr.io/flaresolverr/flaresolverr:latest
restart: unless-stopped
environment:
LOG_LEVEL: info
TZ: UTC
ports:
# Exposed for ad-hoc debugging from the host ; the core
# container reaches it as `http://flaresolverr:8191`.
- "8191:8191"
web:
build: ./web
working_dir: /web
# pnpm itself is baked into the image (see web/Dockerfile). We
# still run install at start because the node_modules volume mount
# would hide anything baked in; `pnpm install` is a no-op when the
# lockfile matches what's already in the volume. `pnpm dev` runs every
# app in parallel (the product app on 3001, the marketing site on 3000,
# the blog on 3002), one install for the whole workspace.
command: sh -c "pnpm install --frozen-lockfile=false && pnpm dev"
environment:
NEXT_PUBLIC_API_URL: http://localhost:8000
NEXT_PUBLIC_API_VERSION: v1
# Cross-app link origins (bare). Set explicitly so the dev port mapping is
# single-sourced here rather than relying on resolveOrigin()'s hardcoded
# fallbacks happening to match these ports.
NEXT_PUBLIC_MARKETING_URL: http://localhost:3000
NEXT_PUBLIC_APP_URL: http://localhost:3001
NEXT_PUBLIC_BLOG_URL: http://localhost:3002
# Base the email-render sidecar uses for brand image URLs in emails. Dev
# points at the local marketing origin so previews load the emblem; prod
# sets it to the public asset host (defaults to the marketing site).
ASSETS_URL: http://localhost:3000
ports:
- "3000:3000"
- "3001:3001"
- "3002:3002"
# email-render sidecar (runs in this container via `pnpm dev`); core
# reaches it at http://web:3010, also published for local curl/preview.
- "3010:3010"
volumes:
- ./web:/web
- magpie_node_modules:/web/node_modules
- magpie_app_node_modules:/web/apps/app/node_modules
- magpie_marketing_node_modules:/web/apps/marketing/node_modules
- magpie_email_render_node_modules:/web/apps/email-render/node_modules
- magpie_blog_node_modules:/web/apps/blog/node_modules
depends_on:
# service_healthy (not the default service_started) means web only
# starts once core's /healthz is green. Without this, web boots
# while Django is still resolving uv venv + running migrations and
# the first SSR fetches eat connection-refused.
core:
condition: service_healthy
volumes:
magpie_pgdata:
magpie_node_modules:
magpie_app_node_modules:
magpie_marketing_node_modules:
magpie_email_render_node_modules:
magpie_blog_node_modules: