Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ build
!.env.example
!.env.local.example

docker/.env.local
docker/backend.env.local
docker/frontend.env.local
docker/seed/*.local

*.log
*.tsbuildinfo
next-env.d.ts
Expand Down
3 changes: 3 additions & 0 deletions backend/src/lib/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ function getClient(): S3Client {
accessKeyId: process.env.R2_ACCESS_KEY_ID!,
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!,
},
// MinIO (and some other S3-compatible servers) require path-style URLs.
// Real R2 works fine with path-style too, so it's safe to leave on.
forcePathStyle: process.env.S3_FORCE_PATH_STYLE === "true",
});
}

Expand Down
29 changes: 29 additions & 0 deletions docker/.env.local.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# =============================================================
# backend.env.local (used by the backend container)
# =============================================================
PORT=3001
FRONTEND_URL=http://localhost:3000
SUPABASE_URL=http://host.docker.internal:54321
SUPABASE_SECRET_KEY=<supabase-service-role-key>

# MinIO — container-internal URL (backend talks to minio via Docker DNS)
R2_ENDPOINT_URL=http://minio:9000
R2_ACCESS_KEY_ID=minioadmin
R2_SECRET_ACCESS_KEY=minioadmin
R2_BUCKET_NAME=mike
S3_FORCE_PATH_STYLE=true

# =============================================================
# frontend.env.local (used by the frontend container)
# =============================================================
NEXT_PUBLIC_SUPABASE_URL=http://localhost:54321
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_DEFAULT_KEY=<supabase-anon-key>
SUPABASE_SECRET_KEY=<supabase-service-role-key>
NEXT_PUBLIC_API_BASE_URL=http://localhost:3001

# MinIO — host-accessible URL (browser fetches from host, not container)
R2_ENDPOINT_URL=http://localhost:9000
R2_ACCESS_KEY_ID=minioadmin
R2_SECRET_ACCESS_KEY=minioadmin
R2_BUCKET_NAME=mike
S3_FORCE_PATH_STYLE=true
75 changes: 75 additions & 0 deletions docker/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Mike – Docker Setup

## Local Development

`setup.sh` provisions the local infrastructure (Supabase + MinIO + schema + seed user) and writes env files. The backend and frontend are run separately on the host.

### Prerequisites

| Tool | Install |
|------|---------|
| Docker (with Compose v2) | https://docs.docker.com/get-docker/ |
| Supabase CLI | https://supabase.com/docs/guides/cli |
| `psql` | https://www.postgresql.org/download/ |
| Node.js 20+ | https://nodejs.org/ |

### Quick Start

```bash
./docker/setup.sh
```

This will:
1. Start Supabase locally via the Supabase CLI
2. Start MinIO via Docker Compose
3. Apply the database schema (`backend/migrations/000_one_shot_schema.sql`)
4. Create a default test user
5. Write `backend/.env` and `frontend/.env.local` with auto-detected keys

Then start the apps in two terminals:

```bash
cd backend && npm install && npm run dev # http://localhost:3001
cd frontend && npm install --legacy-peer-deps && npm run dev # http://localhost:3000
```

Open **http://localhost:3000** and log in with:

```
Email: dev@mike.local
Password: password123
```

### Service URLs

| Service | URL |
|---------|-----|
| App (run locally) | http://localhost:3000 |
| Backend API (run locally) | http://localhost:3001 |
| MinIO console | http://localhost:9001 (minioadmin / minioadmin) |
| Supabase Studio | http://localhost:54323 |

### Re-running setup.sh

`setup.sh` is idempotent — re-running it is safe:
- Supabase `start` is a no-op if already running
- Schema SQL uses `IF NOT EXISTS` guards
- User creation tolerates a 422 (already exists) response
- MinIO bucket creation uses `--ignore-existing`
- Env files are overwritten with fresh values

### Env files

`backend/.env` and `frontend/.env.local` are git-ignored and generated by `setup.sh`. See `backend/.env.example` and `frontend/.env.local.example` for the expected shape.

---

## Production Deployment

Production deployment via Docker Compose is not currently supported. The frontend is intended to be deployed to Cloudflare Workers via [`@opennextjs/cloudflare`](https://opennext.js.org/cloudflare):

```bash
cd frontend && npm run deploy
```

The backend can be deployed to any Node.js host (Fly.io, Railway, a VPS, etc.) — see `backend/.env.example` for required env vars. It expects a hosted Supabase project and S3-compatible storage (e.g. Cloudflare R2).
31 changes: 31 additions & 0 deletions docker/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
services:
minio:
image: minio/minio
command: server /data --console-address ":9001"
ports:
- "9000:9000"
- "9001:9001"
environment:
MINIO_ROOT_USER: minioadmin
MINIO_ROOT_PASSWORD: minioadmin
volumes:
- minio_data:/data
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
interval: 5s
retries: 10

minio-init:
image: minio/mc
depends_on:
minio:
condition: service_healthy
entrypoint: >
/bin/sh -c "
mc alias set local http://minio:9000 minioadmin minioadmin;
mc mb --ignore-existing local/mike;
echo 'Bucket ready';
"

volumes:
minio_data:
6 changes: 6 additions & 0 deletions docker/seed/apply-schema.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/usr/bin/env bash
set -euo pipefail
DB_URL="${1:-postgresql://postgres:postgres@localhost:54322/postgres}"
echo "Applying schema..."
psql "$DB_URL" -f "$(dirname "$0")/../../backend/migrations/000_one_shot_schema.sql"
echo "Schema applied."
16 changes: 16 additions & 0 deletions docker/seed/create-user.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/usr/bin/env bash
set -euo pipefail
SUPABASE_URL="${1:-http://localhost:54321}"
SERVICE_KEY="${2:?SERVICE_KEY required}"
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
-X POST "$SUPABASE_URL/auth/v1/admin/users" \
-H "Authorization: Bearer $SERVICE_KEY" \
-H "apikey: $SERVICE_KEY" \
-H "Content-Type: application/json" \
-d '{"email":"dev@mike.local","password":"password123","email_confirm":true}')
if [ "$HTTP_STATUS" = "200" ] || [ "$HTTP_STATUS" = "201" ] || [ "$HTTP_STATUS" = "422" ]; then
echo "Default user ready (dev@mike.local / password123)"
else
echo "Unexpected status $HTTP_STATUS creating default user" >&2
exit 1
fi
84 changes: 84 additions & 0 deletions docker/setup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#!/usr/bin/env bash
set -euo pipefail

check_dep() {
command -v "$1" &>/dev/null || { echo "ERROR: '$1' not found. $2"; exit 1; }
}

echo "[1/5] Checking dependencies..."
check_dep docker "Install Docker: https://docs.docker.com/get-docker/"
check_dep supabase "Install Supabase CLI: https://supabase.com/docs/guides/cli"
check_dep psql "Install psql: https://www.postgresql.org/download/"

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(dirname "$SCRIPT_DIR")"

echo "[2/5] Starting Supabase..."
cd "$ROOT_DIR"
supabase start

# Parse keys from `supabase status -o env` (machine-readable key=value)
SUPABASE_ENV=$(supabase status -o env 2>/dev/null | grep -E '^[A-Z_]+=')
eval "$(echo "$SUPABASE_ENV" | sed 's/^/SB_/')"

ANON_KEY="${SB_ANON_KEY:-}"
SERVICE_KEY="${SB_SERVICE_ROLE_KEY:-}"
DB_URL="${SB_DB_URL:-}"

if [ -z "$ANON_KEY" ] || [ -z "$SERVICE_KEY" ] || [ -z "$DB_URL" ]; then
echo "ERROR: Failed to parse Supabase status. Run 'supabase status -o env' to debug." >&2
exit 1
fi

echo "[3/5] Starting MinIO (object storage)..."
docker compose -f "$SCRIPT_DIR/docker-compose.yml" up -d minio minio-init

echo "[4/5] Applying database schema..."
bash "$SCRIPT_DIR/seed/apply-schema.sh" "$DB_URL"

echo "[5/5] Creating default user..."
bash "$SCRIPT_DIR/seed/create-user.sh" "http://localhost:54321" "$SERVICE_KEY"

echo ""
echo "Writing env files..."
cat > "$ROOT_DIR/backend/.env" << ENVEOF
PORT=3001
FRONTEND_URL=http://localhost:3000
SUPABASE_URL=http://localhost:54321
SUPABASE_SECRET_KEY=$SERVICE_KEY
R2_ENDPOINT_URL=http://localhost:9000
R2_ACCESS_KEY_ID=minioadmin
R2_SECRET_ACCESS_KEY=minioadmin
R2_BUCKET_NAME=mike
S3_FORCE_PATH_STYLE=true
ENVEOF

cat > "$ROOT_DIR/frontend/.env.local" << ENVEOF
NEXT_PUBLIC_SUPABASE_URL=http://localhost:54321
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_DEFAULT_KEY=$ANON_KEY
SUPABASE_SECRET_KEY=$SERVICE_KEY
NEXT_PUBLIC_API_BASE_URL=http://localhost:3001
R2_ENDPOINT_URL=http://localhost:9000
R2_ACCESS_KEY_ID=minioadmin
R2_SECRET_ACCESS_KEY=minioadmin
R2_BUCKET_NAME=mike
S3_FORCE_PATH_STYLE=true
ENVEOF

echo " wrote backend/.env"
echo " wrote frontend/.env.local"
echo ""
echo "Infrastructure ready."
echo ""
echo " Supabase API: http://localhost:54321"
echo " Supabase Studio: http://localhost:54323"
echo " MinIO API: http://localhost:9000"
echo " MinIO console: http://localhost:9001 (minioadmin / minioadmin)"
echo ""
echo " Login: dev@mike.local"
echo " Passw: password123"
echo ""
echo "Next steps - run the apps in separate terminals:"
echo " cd backend && npm install && npm run dev # http://localhost:3001"
echo " cd frontend && npm install && npm run dev # http://localhost:3000"
echo ""
8 changes: 8 additions & 0 deletions supabase/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Supabase
.branches
.temp

# dotenvx
.env.keys
.env.local
.env.*.local
Loading