Complete Phase 4: Polish, Docker, CI/CD, and deployment readiness

- Fix responsive issues: Card/detail panel use p-4 md:p-8, larger
  progress dots, tighter mobile gaps in Contact section
- Fix lint errors: remove unused vars in BentoCard, AnimatedSection;
  fix explicit-any in ExpandableServiceCard test
- Fix zod-validation-error v4 dependency for eslint-plugin-react-hooks
- Add type-check script to package.json
- Create multi-stage Dockerfile (node:20-alpine build, nginx:alpine serve)
- Create .dockerignore to exclude dev files from build context
- Create nginx.conf with SPA routing, gzip, caching, security headers
- Update docker-compose.yml: build context replaces volume mount,
  preserve Traefik labels, add healthcheck
- Rewrite CI/CD pipeline: Node.js setup, npm ci, type-check, lint,
  test, build, rsync + docker compose build on server
- Add AI-generated images for all placeholder locations
- Archive original index.html as index.html.original

All checks pass: tsc --noEmit, eslint, 222/222 tests, vite build

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-06 00:56:07 -07:00
parent 2a59beeac8
commit eccaf8a60d
26 changed files with 229 additions and 824 deletions

39
Dockerfile Normal file
View File

@ -0,0 +1,39 @@
# =============================================================================
# Stage 1: Build
# =============================================================================
FROM node:20-alpine AS builder
WORKDIR /app
# Copy package files first (for layer caching)
COPY package*.json ./
# Install dependencies
RUN npm ci
# Copy source code
COPY . .
# Build the application
RUN npm run build
# =============================================================================
# Stage 2: Production
# =============================================================================
FROM nginx:alpine AS production
# Copy custom nginx configuration
COPY nginx.conf /etc/nginx/conf.d/default.conf
# Copy built assets from builder stage
COPY --from=builder /app/dist /usr/share/nginx/html
# Expose port 80
EXPOSE 80
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --quiet --tries=1 --spider http://localhost/ || exit 1
# Start nginx
CMD ["nginx", "-g", "daemon off;"]