Phase 1 & 2: full-stack family dashboard scaffold

- pnpm monorepo (apps/client + apps/server)
- Server: Express + node:sqlite with numbered migration runner,
  REST API for all 9 features (members, events, chores, shopping,
  meals, messages, countdowns, photos, settings)
- Client: React 18 + Vite + TypeScript + Tailwind + Framer Motion + Zustand
- Theme system: dark/light + 5 accent colors, CSS custom properties,
  anti-FOUC script, ThemeToggle on every surface
- AppShell: collapsible sidebar, animated route transitions, mobile drawer
- Phase 2 features: Calendar (custom month grid, event chips, add/edit modal),
  Chores (card grid, complete/reset, member filter, streaks),
  Shopping (multi-list tabs, animated check-off, quick-add bar, member assign)
- Family member CRUD with avatar, color picker
- Settings page: theme/accent, photo folder, slideshow, weather, date/time
- Docker: multi-stage Dockerfile, docker-compose.yml, entrypoint with PUID/PGID
- Unraid: CA XML template, CLI install script, UNRAID.md guide
- .gitignore covering node_modules, dist, db files, secrets, build artifacts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-29 21:56:30 -05:00
parent 6e44883365
commit 35ed5223a0
58 changed files with 6224 additions and 0 deletions

88
Dockerfile Normal file
View File

@@ -0,0 +1,88 @@
# ─────────────────────────────────────────────────────────────────────────────
# Stage 1 — Build client
# ─────────────────────────────────────────────────────────────────────────────
FROM node:22-alpine AS client-builder
WORKDIR /build
# Install pnpm
RUN corepack enable && corepack prepare pnpm@latest --activate
# Copy workspace manifests first for better layer caching
COPY pnpm-workspace.yaml package.json pnpm-lock.yaml* ./
COPY apps/client/package.json ./apps/client/
COPY apps/server/package.json ./apps/server/
COPY tsconfig.base.json ./
# Install all workspace deps
RUN pnpm install --frozen-lockfile
# Copy client source and build
COPY apps/client ./apps/client/
RUN pnpm --filter client build
# ─────────────────────────────────────────────────────────────────────────────
# Stage 2 — Build server
# ─────────────────────────────────────────────────────────────────────────────
FROM node:22-alpine AS server-builder
WORKDIR /build
RUN corepack enable && corepack prepare pnpm@latest --activate
COPY pnpm-workspace.yaml package.json pnpm-lock.yaml* ./
COPY apps/server/package.json ./apps/server/
COPY apps/client/package.json ./apps/client/
COPY tsconfig.base.json ./
# Install only server production deps + rebuild native modules for target arch
RUN pnpm install --frozen-lockfile
COPY apps/server ./apps/server/
RUN pnpm --filter server build
# ─────────────────────────────────────────────────────────────────────────────
# Stage 3 — Production runtime
# ─────────────────────────────────────────────────────────────────────────────
FROM node:22-alpine AS runtime
# Install tini for proper PID 1 signal handling
RUN apk add --no-cache tini su-exec
WORKDIR /app
# No native deps — runtime only needs node_modules for pure-JS packages (express, cors, etc.)
# Copy them directly from the builder stage instead of re-installing
COPY --from=server-builder /build/node_modules ./node_modules
COPY --from=server-builder /build/apps/server/node_modules ./apps/server/node_modules 2>/dev/null || true
# Copy compiled server output (includes dist/db/migrations/*.js compiled by tsc)
COPY --from=server-builder /build/apps/server/dist ./apps/server/dist
# Copy built client into the path the server expects
COPY --from=client-builder /build/apps/client/dist ./apps/client/dist
# ── Runtime configuration ─────────────────────────────────────────────────
ENV NODE_ENV=production \
PORT=3001 \
DATA_DIR=/data \
PHOTOS_DIR=/photos \
PUID=99 \
PGID=100 \
TZ=UTC \
NODE_NO_WARNINGS=1
# /data — persistent: SQLite database
# /photos — bind-mount: read-only user photo library
VOLUME ["/data"]
EXPOSE 3001
# Entrypoint: fix ownership then drop to PUID/PGID
COPY docker-entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh
ENTRYPOINT ["/sbin/tini", "--", "/usr/local/bin/entrypoint.sh"]
CMD ["node", "apps/server/dist/index.js"]