Skip to content

Commit 848269a

Browse files
authored
Added support for developing in devcontainer (#480)
1 parent fd59bc2 commit 848269a

File tree

9 files changed

+489
-1
lines changed

9 files changed

+489
-1
lines changed

.devcontainer/Caddyfile.dev

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Like dev/Caddyfile.dev, but LiveKit and Mailpit are referenced by their
2+
# Docker Compose hostnames instead of 127.0.0.1.
3+
4+
{
5+
auto_https off
6+
admin off
7+
}
8+
9+
:48763 {
10+
handle /_caddy_health {
11+
respond "OK" 200
12+
}
13+
14+
@gateway path /gateway /gateway/*
15+
handle @gateway {
16+
uri strip_prefix /gateway
17+
reverse_proxy 127.0.0.1:49107
18+
}
19+
20+
@marketing path /marketing /marketing/*
21+
handle @marketing {
22+
uri strip_prefix /marketing
23+
reverse_proxy 127.0.0.1:49531
24+
}
25+
26+
@server path /admin /admin/* /api /api/* /s3 /s3/* /queue /queue/* /media /media/* /_health /_ready /_live /.well-known/fluxer
27+
handle @server {
28+
reverse_proxy 127.0.0.1:49319
29+
}
30+
31+
@livekit path /livekit /livekit/*
32+
handle @livekit {
33+
uri strip_prefix /livekit
34+
reverse_proxy livekit:7880
35+
}
36+
37+
redir /mailpit /mailpit/
38+
handle_path /mailpit/* {
39+
rewrite * /mailpit{path}
40+
reverse_proxy mailpit:8025
41+
}
42+
43+
handle {
44+
reverse_proxy 127.0.0.1:49427 {
45+
header_up Connection {http.request.header.Connection}
46+
header_up Upgrade {http.request.header.Upgrade}
47+
}
48+
}
49+
50+
log {
51+
output stdout
52+
format console
53+
}
54+
}

.devcontainer/Dockerfile

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Language runtimes (Node.js, Go, Rust, Python) are installed via devcontainer
2+
# features. This Dockerfile handles Erlang/OTP (no feature available) and
3+
# tools like Caddy, process-compose, rebar3, uv, ffmpeg, and exiftool.
4+
5+
FROM erlang:28-slim AS erlang
6+
7+
FROM mcr.microsoft.com/devcontainers/base:debian-13
8+
9+
ARG DEBIAN_FRONTEND=noninteractive
10+
ARG REBAR3_VERSION=3.24.0
11+
ARG PROCESS_COMPOSE_VERSION=1.90.0
12+
13+
# Both erlang:28-slim and debian-13 are Trixie-based, so OpenSSL versions match.
14+
COPY --from=erlang /usr/local/lib/erlang /usr/local/lib/erlang
15+
RUN ln -sf /usr/local/lib/erlang/bin/* /usr/local/bin/
16+
17+
RUN apt-get update && apt-get install -y --no-install-recommends \
18+
libncurses6 libsctp1 \
19+
build-essential pkg-config \
20+
ffmpeg libimage-exiftool-perl \
21+
sqlite3 libsqlite3-dev \
22+
libssl-dev openssl \
23+
gettext-base lsof iproute2 \
24+
&& rm -rf /var/lib/apt/lists/*
25+
26+
RUN curl -fsSL "https://github.com/erlang/rebar3/releases/download/${REBAR3_VERSION}/rebar3" \
27+
-o /usr/local/bin/rebar3 \
28+
&& chmod +x /usr/local/bin/rebar3
29+
30+
RUN curl -fsSL "https://caddyserver.com/api/download?os=linux&arch=amd64" \
31+
-o /usr/local/bin/caddy \
32+
&& chmod +x /usr/local/bin/caddy
33+
34+
RUN curl -fsSL "https://github.com/F1bonacc1/process-compose/releases/download/v${PROCESS_COMPOSE_VERSION}/process-compose_linux_amd64.tar.gz" \
35+
| tar xz -C /usr/local/bin process-compose \
36+
&& chmod +x /usr/local/bin/process-compose
37+
38+
RUN curl -fsSL "https://github.com/astral-sh/uv/releases/latest/download/uv-x86_64-unknown-linux-gnu.tar.gz" \
39+
| tar xz --strip-components=1 -C /usr/local/bin \
40+
&& chmod +x /usr/local/bin/uv /usr/local/bin/uvx

.devcontainer/devcontainer.json

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
{
2+
"name": "Fluxer",
3+
"dockerComposeFile": "docker-compose.yml",
4+
"service": "app",
5+
"workspaceFolder": "/workspace",
6+
7+
"features": {
8+
"ghcr.io/devcontainers/features/node:1": {
9+
"version": "24",
10+
"pnpmVersion": "10.29.3"
11+
},
12+
"ghcr.io/devcontainers/features/go:1": {
13+
"version": "1.24"
14+
},
15+
"ghcr.io/devcontainers/features/rust:1": {
16+
"version": "1.93.0",
17+
"targets": "wasm32-unknown-unknown"
18+
},
19+
"ghcr.io/devcontainers/features/python:1": {
20+
"version": "os-provided",
21+
"installTools": false
22+
}
23+
},
24+
25+
"onCreateCommand": ".devcontainer/on-create.sh",
26+
27+
"remoteEnv": {
28+
"FLUXER_CONFIG": "${containerWorkspaceFolder}/config/config.json",
29+
"FLUXER_DATABASE": "sqlite"
30+
},
31+
32+
"forwardPorts": [48763, 6379, 7700, 7880],
33+
34+
"portsAttributes": {
35+
"48763": {
36+
"label": "Fluxer (Caddy)",
37+
"onAutoForward": "openBrowser",
38+
"protocol": "http"
39+
},
40+
"6379": {
41+
"label": "Valkey",
42+
"onAutoForward": "silent"
43+
},
44+
"7700": {
45+
"label": "Meilisearch",
46+
"onAutoForward": "silent"
47+
},
48+
"7880": {
49+
"label": "LiveKit",
50+
"onAutoForward": "silent"
51+
},
52+
"9229": {
53+
"label": "Node.js Debugger",
54+
"onAutoForward": "silent"
55+
}
56+
},
57+
58+
"customizations": {
59+
"vscode": {
60+
"extensions": [
61+
"TypeScriptTeam.native-preview",
62+
"biomejs.biome",
63+
"clinyong.vscode-css-modules",
64+
"pgourlain.erlang",
65+
"golang.go",
66+
"rust-lang.rust-analyzer"
67+
],
68+
"settings": {
69+
"typescript.preferences.includePackageJsonAutoImports": "auto",
70+
"typescript.suggest.autoImports": true,
71+
"typescript.experimental.useTsgo": true
72+
}
73+
}
74+
}
75+
}

.devcontainer/docker-compose.yml

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
services:
2+
app:
3+
build:
4+
context: .
5+
dockerfile: Dockerfile
6+
volumes:
7+
- ..:/workspace:cached
8+
command: sleep infinity
9+
10+
valkey:
11+
image: valkey/valkey:8-alpine
12+
restart: unless-stopped
13+
command: ['valkey-server', '--appendonly', 'yes', '--save', '60', '1', '--loglevel', 'warning']
14+
volumes:
15+
- valkey-data:/data
16+
healthcheck:
17+
test: ['CMD', 'valkey-cli', 'ping']
18+
interval: 10s
19+
timeout: 5s
20+
retries: 5
21+
22+
meilisearch:
23+
image: getmeili/meilisearch:v1.14
24+
restart: unless-stopped
25+
environment:
26+
MEILI_NO_ANALYTICS: 'true'
27+
MEILI_ENV: development
28+
MEILI_MASTER_KEY: fluxer-devcontainer-meili-master-key
29+
volumes:
30+
- meilisearch-data:/meili_data
31+
healthcheck:
32+
test: ['CMD', 'curl', '-f', 'http://localhost:7700/health']
33+
interval: 10s
34+
timeout: 5s
35+
retries: 5
36+
37+
livekit:
38+
image: livekit/livekit-server:v1.9
39+
restart: unless-stopped
40+
command: --config /etc/livekit.yaml
41+
volumes:
42+
- ./livekit.yaml:/etc/livekit.yaml:ro
43+
44+
mailpit:
45+
image: axllent/mailpit:latest
46+
restart: unless-stopped
47+
command: ['--webroot', '/mailpit/']
48+
49+
nats-core:
50+
image: nats:2-alpine
51+
restart: unless-stopped
52+
command: ['--port', '4222']
53+
54+
nats-jetstream:
55+
image: nats:2-alpine
56+
restart: unless-stopped
57+
command: ['--port', '4223', '--jetstream', '--store_dir', '/data']
58+
volumes:
59+
- nats-jetstream-data:/data
60+
61+
volumes:
62+
valkey-data:
63+
meilisearch-data:
64+
nats-jetstream-data:

.devcontainer/livekit.yaml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Credentials here must match the values on-create.sh writes to config.json.
2+
3+
port: 7880
4+
5+
keys:
6+
fluxer-devcontainer-key: fluxer-devcontainer-secret-key-00000000
7+
8+
rtc:
9+
tcp_port: 7881
10+
port_range_start: 50000
11+
port_range_end: 50100
12+
use_external_ip: false
13+
node_ip: 127.0.0.1
14+
15+
turn:
16+
enabled: true
17+
domain: localhost
18+
udp_port: 3478
19+
20+
webhook:
21+
api_key: fluxer-devcontainer-key
22+
urls:
23+
- http://app:49319/api/webhooks/livekit
24+
25+
room:
26+
auto_create: true
27+
max_participants: 100
28+
empty_timeout: 300
29+
30+
development: true

.devcontainer/on-create.sh

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
#!/usr/bin/env bash
2+
3+
# Runs once when the container is first created.
4+
5+
set -euo pipefail
6+
7+
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
8+
export FLUXER_CONFIG="${FLUXER_CONFIG:-$REPO_ROOT/config/config.json}"
9+
10+
GREEN='\033[0;32m'
11+
NC='\033[0m'
12+
info() { printf "%b\n" "${GREEN}[devcontainer]${NC} $1"; }
13+
14+
info "Installing pnpm dependencies..."
15+
pnpm install
16+
17+
# Codegen outputs (e.g. MasterZodSchema.generated.tsx) are gitignored.
18+
info "Generating config schema..."
19+
pnpm --filter @fluxer/config generate
20+
21+
if [ ! -f "$FLUXER_CONFIG" ]; then
22+
info "Creating config from development template..."
23+
cp "$REPO_ROOT/config/config.dev.template.json" "$FLUXER_CONFIG"
24+
fi
25+
26+
# Point services at Docker Compose hostnames and adjust settings that differ
27+
# from the default dev template.
28+
info "Patching config for Docker Compose networking..."
29+
jq '
30+
# rspack defaults public_scheme to "https" when unset
31+
.domain.public_scheme = "http" |
32+
# Relative path so the app works on any hostname (localhost, 127.0.0.1, etc.)
33+
.app_public.bootstrap_api_endpoint = "/api" |
34+
35+
.internal.kv = "redis://valkey:6379/0" |
36+
37+
.integrations.search.url = "http://meilisearch:7700" |
38+
.integrations.search.api_key = "fluxer-devcontainer-meili-master-key" |
39+
40+
# Credentials must match .devcontainer/livekit.yaml
41+
.integrations.voice.url = "ws://livekit:7880" |
42+
.integrations.voice.webhook_url = "http://app:49319/api/webhooks/livekit" |
43+
.integrations.voice.api_key = "fluxer-devcontainer-key" |
44+
.integrations.voice.api_secret = "fluxer-devcontainer-secret-key-00000000" |
45+
46+
.integrations.email.smtp.host = "mailpit" |
47+
.integrations.email.smtp.port = 1025 |
48+
49+
.services.nats.core_url = "nats://nats-core:4222" |
50+
.services.nats.jetstream_url = "nats://nats-jetstream:4223" |
51+
52+
# Bluesky OAuth requires HTTPS + loopback IPs (RFC 8252), incompatible with
53+
# the HTTP-only devcontainer setup.
54+
.auth.bluesky.enabled = false
55+
' "$FLUXER_CONFIG" > "$FLUXER_CONFIG.tmp" && mv "$FLUXER_CONFIG.tmp" "$FLUXER_CONFIG"
56+
57+
info "Running bootstrap..."
58+
"$REPO_ROOT/scripts/dev_bootstrap.sh"
59+
60+
info "Pre-compiling Erlang gateway dependencies..."
61+
(cd "$REPO_ROOT/fluxer_gateway" && rebar3 compile) || {
62+
info "Gateway pre-compilation failed (non-fatal, will compile on first start)"
63+
}
64+
65+
info "Devcontainer setup complete."
66+
info ""
67+
info " Start all dev processes: process-compose -f .devcontainer/process-compose.yml up"
68+
info " Open the app: http://127.0.0.1:48763"
69+
info " Dev email inbox: http://127.0.0.1:48763/mailpit/"
70+
info ""

.devcontainer/process-compose.yml

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Application processes only — backing services (Valkey, Meilisearch, LiveKit,
2+
# Mailpit, NATS) run via Docker Compose.
3+
# process-compose -f .devcontainer/process-compose.yml up
4+
5+
is_tui_disabled: false
6+
log_level: info
7+
log_configuration:
8+
flush_each_line: true
9+
10+
processes:
11+
caddy:
12+
command: caddy run --config .devcontainer/Caddyfile.dev --adapter caddyfile
13+
log_location: dev/logs/caddy.log
14+
readiness_probe:
15+
http_get:
16+
host: 127.0.0.1
17+
port: 48763
18+
path: /_caddy_health
19+
availability:
20+
restart: always
21+
22+
fluxer_server:
23+
command: pnpm --filter fluxer_server dev
24+
log_location: dev/logs/fluxer_server.log
25+
availability:
26+
restart: always
27+
28+
fluxer_app:
29+
command: ./scripts/dev_fluxer_app.sh
30+
environment:
31+
- FORCE_COLOR=1
32+
- FLUXER_APP_DEV_PORT=49427
33+
log_location: dev/logs/fluxer_app.log
34+
availability:
35+
restart: always
36+
37+
fluxer_gateway:
38+
command: ./scripts/dev_gateway.sh
39+
environment:
40+
- FLUXER_GATEWAY_NO_SHELL=1
41+
log_location: dev/logs/fluxer_gateway.log
42+
availability:
43+
restart: always
44+
45+
marketing_dev:
46+
command: pnpm --filter fluxer_marketing dev
47+
environment:
48+
- FORCE_COLOR=1
49+
log_location: dev/logs/marketing_dev.log
50+
availability:
51+
restart: always
52+
53+
css_watch:
54+
command: ./scripts/dev_css_watch.sh
55+
log_location: dev/logs/css_watch.log
56+
availability:
57+
restart: always

0 commit comments

Comments
 (0)