Rebuild deploy process on the project template
- Dockerfile: deps → builder → pb-downloader → runner stages, cross-arch (BUILDPLATFORM node stages, TARGETARCH PocketBase binary), Debian slim runner, VOLUME /app/pb_data, dual healthcheck, docker-entrypoint.sh - docker-entrypoint.sh: bash supervisor replacing scripts/run-all.mjs - Makefile: build-production (multi-arch push from git remote), build-check, build-check-local - deploy.sh [tag]: make build-production then ansible-playbook with tag - drop .env/.env.example (credentials live in ansible/vars/default.yml)
This commit is contained in:
+5
-1
@@ -16,5 +16,9 @@ app/dist
|
||||
|
||||
# Ops assets not needed inside the image
|
||||
ansible
|
||||
scripts/create-collections.sh
|
||||
ansible-sample
|
||||
Dockerfile-sample
|
||||
deploy-sample.sh
|
||||
Makefile
|
||||
README.md
|
||||
scripts/create-collections.sh
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
# Copy to .env and fill in real values. .env is git-ignored.
|
||||
REGISTRY=registry.tanshu.com
|
||||
REGISTRY_USER=ta-registry
|
||||
REGISTRY_PASSWORD=changeme
|
||||
|
||||
# Initial PocketBase admin (applied on first container start; idempotent)
|
||||
PB_SUPERUSER_EMAIL=admin@greatbear.in
|
||||
PB_SUPERUSER_PASSWORD=changeme
|
||||
@@ -26,3 +26,5 @@ ansible-sample/
|
||||
*.log
|
||||
.idea/
|
||||
.vscode/
|
||||
Dockerfile-sample
|
||||
deploy-sample.sh
|
||||
|
||||
+67
-40
@@ -1,61 +1,88 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
# The Great Bear — single image running TanStack Start (site) + PocketBase (CMS/API)
|
||||
|
||||
ARG NODE_VERSION=22
|
||||
ARG PB_VERSION=0.40.2
|
||||
# ------------------------------------------------------------------------------
|
||||
# Stage 1: Install frontend dependencies (Native execution on host architecture)
|
||||
# ------------------------------------------------------------------------------
|
||||
FROM --platform=$BUILDPLATFORM node:lts-trixie-slim AS deps
|
||||
WORKDIR /work
|
||||
|
||||
# ---------- Stage 1: build the TanStack Start app ----------
|
||||
FROM node:${NODE_VERSION}-alpine AS build
|
||||
WORKDIR /app
|
||||
COPY app/package.json app/package-lock.json* ./
|
||||
RUN --mount=type=cache,target=/root/.npm \
|
||||
npm ci
|
||||
|
||||
COPY app/package.json app/package-lock.json ./
|
||||
RUN npm ci --no-audit --no-fund
|
||||
# ------------------------------------------------------------------------------
|
||||
# Stage 2: Build the TanStack Start frontend (Native execution on host architecture)
|
||||
# ------------------------------------------------------------------------------
|
||||
FROM --platform=$BUILDPLATFORM node:lts-trixie-slim AS builder
|
||||
WORKDIR /work
|
||||
|
||||
COPY --from=deps /work/node_modules ./node_modules
|
||||
COPY app/ ./
|
||||
|
||||
ENV NODE_ENV=production
|
||||
RUN npm run build
|
||||
|
||||
# ---------- Stage 2: runtime ----------
|
||||
FROM node:${NODE_VERSION}-alpine AS runtime
|
||||
ARG PB_VERSION
|
||||
# ------------------------------------------------------------------------------
|
||||
# Stage 3: Fetch PocketBase binary for target architecture (Native host downloader)
|
||||
# ------------------------------------------------------------------------------
|
||||
FROM --platform=$BUILDPLATFORM alpine:3.20 AS pb-downloader
|
||||
ARG TARGETARCH
|
||||
ARG PB_VERSION=0.40.2
|
||||
|
||||
ENV NODE_ENV=production \
|
||||
PORT=3000 \
|
||||
PB_URL=http://127.0.0.1:8090
|
||||
RUN apk add --no-cache curl unzip ca-certificates \
|
||||
&& case "${TARGETARCH}" in \
|
||||
amd64) PB_ARCH="amd64" ;; \
|
||||
arm64) PB_ARCH="arm64" ;; \
|
||||
arm) PB_ARCH="armv7" ;; \
|
||||
*) echo "Unsupported target architecture: ${TARGETARCH}" >&2; exit 1 ;; \
|
||||
esac \
|
||||
&& curl -fsSL -o /tmp/pb.zip "https://github.com/pocketbase/pocketbase/releases/download/v${PB_VERSION}/pocketbase_${PB_VERSION}_linux_${PB_ARCH}.zip" \
|
||||
&& unzip /tmp/pb.zip -d /tmp/pb \
|
||||
&& chmod +x /tmp/pb/pocketbase \
|
||||
&& rm -f /tmp/pb.zip
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Stage 4: Production runner (Target architecture)
|
||||
# ------------------------------------------------------------------------------
|
||||
FROM node:lts-trixie-slim AS runner
|
||||
LABEL maintainer="Amritanshu <docker@tanshu.com>"
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
curl \
|
||||
ca-certificates \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN apk add --no-cache curl unzip
|
||||
# Copy PocketBase binary and migrations
|
||||
COPY --from=pb-downloader /tmp/pb/pocketbase /app/pocketbase
|
||||
COPY pb/migrations /app/pb_migrations
|
||||
RUN mkdir -p /app/pb_public
|
||||
|
||||
# Site server build output
|
||||
COPY --from=build /app/.output ./.output
|
||||
# Copy compiled frontend application
|
||||
COPY --from=builder /work/.output /app/.output
|
||||
|
||||
# Content seed (shared with the frontend fallback) + process supervisor
|
||||
COPY app/src/data/content.json ./scripts/content.json
|
||||
COPY scripts/seed.mjs scripts/run-all.mjs ./scripts/
|
||||
# Copy content seed + entrypoint script
|
||||
COPY app/src/data/content.json /app/scripts/content.json
|
||||
COPY scripts/seed.mjs /app/scripts/seed.mjs
|
||||
COPY docker-entrypoint.sh /app/docker-entrypoint.sh
|
||||
RUN chmod +x /app/docker-entrypoint.sh
|
||||
|
||||
# PocketBase schema migrations
|
||||
COPY pb/migrations ./pb_migrations
|
||||
RUN mkdir -p pb_public
|
||||
# Environment variables
|
||||
ENV NODE_ENV=production \
|
||||
PORT=3000 \
|
||||
HOST=0.0.0.0 \
|
||||
PB_DATA_DIR=/app/pb_data
|
||||
|
||||
# PocketBase binary (static Go binary, runs fine on Alpine)
|
||||
RUN case "${TARGETARCH:-amd64}" in \
|
||||
arm64) PB_ARCH=arm64 ;; \
|
||||
*) PB_ARCH=amd64 ;; \
|
||||
esac \
|
||||
&& curl -fsSL -o /tmp/pb.zip \
|
||||
"https://github.com/pocketbase/pocketbase/releases/download/v${PB_VERSION}/pocketbase_${PB_VERSION}_linux_${PB_ARCH}.zip" \
|
||||
&& unzip -o /tmp/pb.zip pocketbase -d /app \
|
||||
&& chmod +x /app/pocketbase \
|
||||
&& rm /tmp/pb.zip \
|
||||
&& mkdir -p /app/pb_data
|
||||
|
||||
# Runs as root so the Ansible-managed bind mount (/var/lib/greatbear/pb_data)
|
||||
# is writable regardless of host-side ownership.
|
||||
# Persistent data volume for PocketBase SQLite database and file storage
|
||||
VOLUME ["/app/pb_data"]
|
||||
|
||||
# Port 3000: TanStack Start Web Application
|
||||
# Port 8090: PocketBase REST API & Admin UI
|
||||
EXPOSE 3000 8090
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=5 \
|
||||
CMD curl -fsS http://127.0.0.1:8090/api/health -o /dev/null || exit 1
|
||||
# Health check for both PocketBase and frontend server
|
||||
HEALTHCHECK --interval=15s --timeout=3s --start-period=5s --retries=3 \
|
||||
CMD curl -fsS http://127.0.0.1:8090/api/health > /dev/null && curl -fsS http://127.0.0.1:3000 > /dev/null || exit 1
|
||||
|
||||
CMD ["node", "scripts/run-all.mjs"]
|
||||
ENTRYPOINT ["/app/docker-entrypoint.sh"]
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
.PHONY: build-production
|
||||
build-production: ## Build the production docker image and push to private registry
|
||||
@docker buildx build \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--tag registry.tanshu.com/tanshu/greatbear:latest \
|
||||
$(if $(filter-out latest,$(TAG)),--tag registry.tanshu.com/tanshu/greatbear:$(TAG)) \
|
||||
--push \
|
||||
--pull \
|
||||
git@git.tanshu.com:tanshu/greatbear.git
|
||||
|
||||
.PHONY: build-check
|
||||
build-check: ## Multi-arch build without push (compile check)
|
||||
@docker buildx build \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--tag greatbear:test \
|
||||
--pull \
|
||||
--progress=plain \
|
||||
git@git.tanshu.com:tanshu/greatbear.git
|
||||
|
||||
.PHONY: build-check-local
|
||||
build-check-local: ## Build from the committed local tree (git archive) and load into docker
|
||||
@git archive --format=tar HEAD | docker buildx build \
|
||||
--platform linux/amd64 \
|
||||
--tag greatbear:test \
|
||||
--pull \
|
||||
--progress=plain \
|
||||
--load \
|
||||
-
|
||||
@@ -9,7 +9,7 @@ Stitch "Industrial Hospitality" design.
|
||||
| Backend | [PocketBase](https://pocketbase.io) v0.40 — CMS content, form intake, admin UI |
|
||||
| Runtime | Single Docker image running both services |
|
||||
| Proxy | Caddy (automatic HTTPS) on the target host |
|
||||
| Deploys | `deploy.sh` (build + push) → Ansible playbook (pull + run) |
|
||||
| Deploys | `deploy.sh [tag]` (Makefile buildx push) → Ansible playbook |
|
||||
|
||||
## Pages
|
||||
|
||||
@@ -33,12 +33,13 @@ app/ TanStack Start application
|
||||
public/images/ Design assets (downloaded from the Stitch export)
|
||||
pb/migrations/ PocketBase schema (snapshot migration, auto-applied)
|
||||
scripts/
|
||||
run-all.mjs Container supervisor: PocketBase + site server
|
||||
seed.mjs Idempotent content seeder (create-if-missing by uid)
|
||||
create-collections.sh Dev helper used to build the schema migration
|
||||
ansible/ Deployment playbook (roles: network, greatbear, caddy)
|
||||
Dockerfile Multi-stage image build
|
||||
deploy.sh Build & push image to the private registry
|
||||
Makefile Multi-arch buildx targets (build-production / build-check)
|
||||
Dockerfile Multi-stage image build (deps → builder → pb-downloader → runner)
|
||||
docker-entrypoint.sh Container supervisor: PocketBase + site server
|
||||
deploy.sh Build & push image, then run the Ansible deploy
|
||||
```
|
||||
|
||||
## Local development
|
||||
@@ -86,16 +87,25 @@ docker run --rm -p 3000:3000 -p 8090:8090 \
|
||||
|
||||
## Deploying
|
||||
|
||||
### 1. Build and push the image
|
||||
The image is always built from the **committed state of the git remote** — commit and push
|
||||
before deploying. Registry credentials: `docker login registry.tanshu.com` (once per machine).
|
||||
|
||||
```bash
|
||||
cp .env.example .env # then fill in registry credentials
|
||||
./deploy.sh
|
||||
./deploy.sh # build + push :latest, then deploy that tag via Ansible
|
||||
./deploy.sh v1.2.3 # same, with an explicit version tag
|
||||
```
|
||||
|
||||
Tags pushed: `registry.tanshu.com/tanshu/greatbear:latest` and `:<git-sha>`.
|
||||
Or run the pieces yourself:
|
||||
|
||||
### 2. Deploy with Ansible
|
||||
```bash
|
||||
make build-production TAG=v1.2.3 # multi-arch (amd64+arm64) buildx build & push
|
||||
make build-check # multi-arch compile check (no push)
|
||||
make build-check-local # build the committed local tree (git archive) and load it
|
||||
|
||||
cd ansible && ansible-playbook playbook.yml -e "tag=v1.2.3"
|
||||
```
|
||||
|
||||
### Ansible deployment
|
||||
|
||||
Requirements on the controller: `ansible` + `community.docker` collection (see
|
||||
`ansible/requirements.yml`). Requirements on the host: Docker with a Caddy **container** named
|
||||
@@ -107,10 +117,11 @@ ansible-galaxy install -r requirements.yml
|
||||
ansible-playbook playbook.yml # targets inventory host `monoco` — edit playbook.yml if needed
|
||||
```
|
||||
|
||||
All deployment variables live in `ansible/vars/default.yml`. The playbook:
|
||||
All deployment variables live in `ansible/vars/default.yml` (registry, tag, domains,
|
||||
PocketBase admin credentials). The playbook:
|
||||
|
||||
1. Ensures the `greatbear_net` Docker network exists with the Caddy container attached.
|
||||
2. Pulls `registry.tanshu.com/tanshu/greatbear:latest`, uploads `/var/lib/greatbear/.env`,
|
||||
2. Pulls `registry.tanshu.com/tanshu/greatbear:{tag}`, uploads `/var/lib/greatbear/.env`,
|
||||
and (re)creates the `greatbear` container on that network with
|
||||
`/var/lib/greatbear/pb_data` bind-mounted to `/app/pb_data`, then waits for its healthcheck.
|
||||
3. Inserts a managed snippet into the shared Caddyfile and reloads Caddy via
|
||||
@@ -129,7 +140,8 @@ automatically on first request.
|
||||
|
||||
- URL: `https://admin.greatbear.in/_/`
|
||||
- First container start bootstraps the superuser from `PB_SUPERUSER_EMAIL` /
|
||||
`PB_SUPERUSER_PASSWORD` (set in `ansible/group_vars/greatbear/vars.yml`, idempotent).
|
||||
`PB_SUPERUSER_PASSWORD` (templated from `pb_admin_email` / `pb_admin_password` in
|
||||
`ansible/vars/default.yml`, idempotent).
|
||||
|
||||
Content collections: `brews`, `menu_categories`, `menu_items`, `experiences`, `testimonials`,
|
||||
`stats`, `pairings`, `settings`. Reservation/contact submissions land in `reservations`
|
||||
@@ -143,7 +155,8 @@ are never overwritten by redeploys. Schema changes belong in `pb/migrations/`.
|
||||
| Variable | Default | Purpose |
|
||||
| ----------------------- | ------------------------ | -------------------------------------- |
|
||||
| `PORT` | `3000` | Site server port |
|
||||
| `HOST` | `0.0.0.0` | Site server bind address |
|
||||
| `PB_URL` | `http://127.0.0.1:8090` | PocketBase URL used by the site server |
|
||||
| `PB_DATA_DIR` | `/data` | PocketBase data directory (volume) |
|
||||
| `PB_DATA_DIR` | `/app/pb_data` | PocketBase data directory (volume) |
|
||||
| `PB_SUPERUSER_EMAIL` | — | Initial admin (bootstrapped at start) |
|
||||
| `PB_SUPERUSER_PASSWORD` | — | Initial admin password |
|
||||
|
||||
@@ -1,40 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Build the Great Bear image and push it to the private registry.
|
||||
#
|
||||
# Reads registry credentials from the environment or from ./.env
|
||||
# (REGISTRY_USER, REGISTRY_PASSWORD). Optional overrides: REGISTRY,
|
||||
# IMAGE_NAME, PLATFORM.
|
||||
#
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
[ -f .env ] && set -a && . ./.env && set +a
|
||||
parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" || exit ; pwd -P )
|
||||
cd "$parent_path" || exit
|
||||
|
||||
REGISTRY="${REGISTRY:-registry.tanshu.com}"
|
||||
IMAGE_NAME="${IMAGE_NAME:-tanshu/greatbear}"
|
||||
REGISTRY_USER="${REGISTRY_USER:?REGISTRY_USER not set (define it in .env or the environment)}"
|
||||
REGISTRY_PASSWORD="${REGISTRY_PASSWORD:?REGISTRY_PASSWORD not set (define it in .env or the environment)}"
|
||||
PLATFORM="${PLATFORM:-linux/amd64}"
|
||||
echo "========================================================"
|
||||
echo " Building & Uploading Docker Image"
|
||||
echo "========================================================"
|
||||
current_version="${1:-latest}"
|
||||
make build-production TAG="$current_version"
|
||||
|
||||
IMAGE="$REGISTRY/$IMAGE_NAME"
|
||||
VERSION="$(git rev-parse --short HEAD 2>/dev/null || date +%Y%m%d%H%M%S)"
|
||||
|
||||
echo "==> Building $IMAGE:$VERSION (and :latest) for $PLATFORM"
|
||||
docker build --platform "$PLATFORM" \
|
||||
-t "$IMAGE:latest" \
|
||||
-t "$IMAGE:$VERSION" \
|
||||
.
|
||||
|
||||
echo "==> Logging in to $REGISTRY"
|
||||
printf '%s' "$REGISTRY_PASSWORD" | docker login "$REGISTRY" --username "$REGISTRY_USER" --password-stdin
|
||||
|
||||
echo "==> Pushing $IMAGE:latest"
|
||||
docker push "$IMAGE:latest"
|
||||
echo "==> Pushing $IMAGE:$VERSION"
|
||||
docker push "$IMAGE:$VERSION"
|
||||
|
||||
echo
|
||||
echo "Done. Deploy with:"
|
||||
echo " cd ansible && ansible-playbook playbook.yml"
|
||||
echo ""
|
||||
echo "========================================================"
|
||||
echo " Executing Ansible Deployment"
|
||||
echo "========================================================"
|
||||
cd "$parent_path/ansible" || exit
|
||||
ansible-playbook playbook.yml -e "tag=$current_version"
|
||||
|
||||
Executable
+61
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Container entrypoint: runs PocketBase and the TanStack Start site server
|
||||
# side by side.
|
||||
# 1. starts PocketBase (data in $PB_DATA_DIR, migrations from ./pb_migrations)
|
||||
# 2. waits for its health endpoint
|
||||
# 3. bootstraps the superuser account (idempotent upsert)
|
||||
# 4. seeds initial content (idempotent, create-if-missing)
|
||||
# 5. starts the site server
|
||||
#
|
||||
# If either process dies the container exits non-zero so Docker restarts it.
|
||||
set -e
|
||||
|
||||
PB_DATA="${PB_DATA_DIR:-/app/pb_data}"
|
||||
PB_HTTP="${PB_HTTP:-0.0.0.0:8090}"
|
||||
|
||||
pb_pid=""
|
||||
site_pid=""
|
||||
|
||||
cleanup() {
|
||||
[ -n "$site_pid" ] && kill "$site_pid" 2>/dev/null || true
|
||||
[ -n "$pb_pid" ] && kill "$pb_pid" 2>/dev/null || true
|
||||
wait 2>/dev/null || true
|
||||
}
|
||||
trap cleanup INT TERM
|
||||
|
||||
echo "[entrypoint] starting PocketBase…"
|
||||
./pocketbase serve \
|
||||
--http="$PB_HTTP" \
|
||||
--dir="$PB_DATA" \
|
||||
--migrationsDir=./pb_migrations \
|
||||
--publicDir=./pb_public &
|
||||
pb_pid=$!
|
||||
|
||||
echo "[entrypoint] waiting for PocketBase health…"
|
||||
for _ in $(seq 1 120); do
|
||||
if curl -fsS http://127.0.0.1:8090/api/health > /dev/null 2>&1; then
|
||||
break
|
||||
fi
|
||||
sleep 0.5
|
||||
done
|
||||
|
||||
if [ -n "${PB_SUPERUSER_EMAIL:-}" ] && [ -n "${PB_SUPERUSER_PASSWORD:-}" ]; then
|
||||
./pocketbase superuser upsert "$PB_SUPERUSER_EMAIL" "$PB_SUPERUSER_PASSWORD" \
|
||||
--dir="$PB_DATA" --migrationsDir=./pb_migrations || true
|
||||
echo "[entrypoint] superuser ensured"
|
||||
node scripts/seed.mjs || echo "[entrypoint] seed failed (continuing)"
|
||||
else
|
||||
echo "[entrypoint] PB_SUPERUSER_EMAIL/PASSWORD not set — skipping superuser + seed"
|
||||
fi
|
||||
|
||||
echo "[entrypoint] starting site server on :${PORT}…"
|
||||
node .output/server/index.mjs &
|
||||
site_pid=$!
|
||||
|
||||
set +e
|
||||
wait -n "$pb_pid" "$site_pid"
|
||||
exit_code=$?
|
||||
echo "[entrypoint] a child process exited with code $exit_code — shutting down"
|
||||
cleanup
|
||||
exit "$exit_code"
|
||||
@@ -1,116 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Container entrypoint supervisor.
|
||||
*
|
||||
* Runs PocketBase and the TanStack Start node server side by side:
|
||||
* 1. starts PocketBase (data in /data, migrations from ./pb_migrations)
|
||||
* 2. waits for its health endpoint
|
||||
* 3. bootstraps the superuser account (idempotent upsert)
|
||||
* 4. seeds initial content (idempotent, create-if-missing)
|
||||
* 5. starts the site server on PORT (default 3000)
|
||||
*
|
||||
* If either process dies the container exits non-zero so Docker restarts it.
|
||||
*/
|
||||
import { spawn, spawnSync } from 'node:child_process'
|
||||
|
||||
const PORT = process.env.PORT ?? '3000'
|
||||
const PB_HTTP = process.env.PB_HTTP ?? '0.0.0.0:8090'
|
||||
// PB_DATA_DIR is the canonical variable (set in the deploy .env); PB_DATA kept
|
||||
// as a legacy alias, /data as the image default.
|
||||
const PB_DATA = process.env.PB_DATA_DIR ?? process.env.PB_DATA ?? '/data'
|
||||
|
||||
let stopping = false
|
||||
const children = []
|
||||
|
||||
function start(name, command, args, extraEnv = {}) {
|
||||
const child = spawn(command, args, {
|
||||
stdio: 'inherit',
|
||||
env: { ...process.env, ...extraEnv },
|
||||
})
|
||||
child.name = name
|
||||
children.push(child)
|
||||
child.on('exit', (code) => {
|
||||
if (stopping) return
|
||||
console.error(`[supervisor] ${name} exited with code ${code} — shutting down`)
|
||||
shutdown(code ?? 1)
|
||||
})
|
||||
return child
|
||||
}
|
||||
|
||||
function run(name, command, args, extraEnv = {}) {
|
||||
const result = spawnSync(command, args, {
|
||||
stdio: 'inherit',
|
||||
env: { ...process.env, ...extraEnv },
|
||||
})
|
||||
if (result.status !== 0) {
|
||||
console.warn(`[supervisor] ${name} finished with status ${result.status}`)
|
||||
}
|
||||
return result.status ?? 0
|
||||
}
|
||||
|
||||
function shutdown(exitCode = 0) {
|
||||
if (stopping) return
|
||||
stopping = true
|
||||
for (const child of children) {
|
||||
if (child.exitCode === null && child.signalCode === null) child.kill('SIGTERM')
|
||||
}
|
||||
setTimeout(() => process.exit(exitCode), 2000)
|
||||
}
|
||||
|
||||
process.on('SIGINT', () => shutdown(0))
|
||||
process.on('SIGTERM', () => shutdown(0))
|
||||
|
||||
async function waitForHealth(url, attempts = 120) {
|
||||
for (let i = 0; i < attempts; i++) {
|
||||
try {
|
||||
const res = await fetch(url)
|
||||
if (res.ok) return
|
||||
} catch {
|
||||
// not up yet
|
||||
}
|
||||
await new Promise((resolve) => setTimeout(resolve, 500))
|
||||
}
|
||||
throw new Error(`health check at ${url} timed out`)
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log('[supervisor] starting PocketBase…')
|
||||
start('pocketbase', './pocketbase', [
|
||||
'serve',
|
||||
`--http=${PB_HTTP}`,
|
||||
`--dir=${PB_DATA}`,
|
||||
'--migrationsDir=./pb_migrations',
|
||||
'--publicDir=./pb_public',
|
||||
])
|
||||
|
||||
await waitForHealth('http://127.0.0.1:8090/api/health')
|
||||
console.log('[supervisor] PocketBase is healthy')
|
||||
|
||||
if (process.env.PB_SUPERUSER_EMAIL && process.env.PB_SUPERUSER_PASSWORD) {
|
||||
run('superuser upsert', './pocketbase', [
|
||||
'superuser',
|
||||
'upsert',
|
||||
process.env.PB_SUPERUSER_EMAIL,
|
||||
process.env.PB_SUPERUSER_PASSWORD,
|
||||
`--dir=${PB_DATA}`,
|
||||
'--migrationsDir=./pb_migrations',
|
||||
])
|
||||
console.log('[supervisor] superuser ensured')
|
||||
|
||||
run('seed', 'node', ['scripts/seed.mjs'])
|
||||
} else {
|
||||
console.warn('[supervisor] PB_SUPERUSER_EMAIL/PASSWORD not set — skipping superuser + seed')
|
||||
}
|
||||
|
||||
console.log(`[supervisor] starting site server on :${PORT}…`)
|
||||
start('site', 'node', ['.output/server/index.mjs'], {
|
||||
PORT,
|
||||
PB_URL: process.env.PB_URL ?? 'http://127.0.0.1:8090',
|
||||
NODE_ENV: 'production',
|
||||
})
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error(`[supervisor] fatal: ${error.message}`)
|
||||
shutdown(1)
|
||||
})
|
||||
Reference in New Issue
Block a user