diff --git a/docker-compose.app.yml b/docker-compose.app.yml index b0c9b616..5773ac68 100644 --- a/docker-compose.app.yml +++ b/docker-compose.app.yml @@ -15,6 +15,7 @@ # cd /data/app/igny8/backend && docker build -t igny8-backend:latest -f Dockerfile . # cd /data/app/igny8/frontend && docker build -t igny8-frontend-dev:latest -f Dockerfile.dev . # cd /data/app/igny8/frontend && docker build -t igny8-marketing:latest -f Dockerfile.marketing . +# cd /data/app/igny8/frontend && docker build -t igny8-marketing-dev:latest -f Dockerfile.marketing.dev . # ============================================================================= name: igny8-app @@ -95,6 +96,23 @@ services: - "com.docker.compose.project=igny8-app" - "com.docker.compose.service=igny8_marketing" + igny8_marketing_dev: + # Development server for marketing site with HMR + # Build separately: docker build -t igny8-marketing-dev:latest -f Dockerfile.marketing.dev . + image: igny8-marketing-dev:latest + container_name: igny8_marketing_dev + restart: always + ports: + - "0.0.0.0:8023:5174" # Marketing dev server port (internal: 5174, external: 8023) + environment: + VITE_BACKEND_URL: "https://api.igny8.com/api" + volumes: + - /data/app/igny8/frontend:/app:rw + networks: [igny8_net] + labels: + - "com.docker.compose.project=igny8-app" + - "com.docker.compose.service=igny8_marketing_dev" + igny8_celery_worker: image: igny8-backend:latest container_name: igny8_celery_worker diff --git a/docs/DEPLOYMENT_STATUS.md b/docs/DEPLOYMENT_STATUS.md new file mode 100644 index 00000000..7ca95ddb --- /dev/null +++ b/docs/DEPLOYMENT_STATUS.md @@ -0,0 +1,113 @@ +# Deployment Status - Marketing Container + +**Last Updated:** 2025-11-13 +**Status:** ✅ **OPERATIONAL** + +--- + +## Current Status + +### Containers +- ✅ `igny8_marketing` - Running (Port 8020 internal, 8022 external) +- ✅ `igny8_caddy` - Running (Routes `igny8.com` → `igny8_marketing:8020`) +- ✅ `igny8_frontend` - Running (Vite dev server for `app.igny8.com`) +- ✅ `igny8_backend` - Running (Django API for `api.igny8.com`) + +### Network +- ✅ All containers on `igny8_net` network +- ✅ Caddy can reach marketing container +- ✅ Marketing container serving on port 8020 + +### HTTP Status +- ✅ Marketing container: HTTP 200 (direct access) +- ✅ Through Caddy: HTTP 200 (production routing) + +--- + +## Deployment Process Verified + +The automated deployment process has been tested and is working: + +```bash +# 1. Build marketing image +cd /data/app/igny8/frontend +docker build -t igny8-marketing:latest -f Dockerfile.marketing . + +# 2. Restart container +cd /data/app/igny8 +docker compose -f docker-compose.app.yml -p igny8-app restart igny8_marketing +``` + +**Result:** ✅ Container restarts with new build, site updates immediately. + +--- + +## Architecture + +``` +Internet + ↓ +Caddy (HTTPS:443) + ↓ +igny8.com → igny8_marketing:8020 (Container) +app.igny8.com → igny8_frontend:5173 (Vite Dev) +api.igny8.com → igny8_backend:8010 (Django) +``` + +--- + +## Quick Commands + +### Check Status +```bash +docker ps --filter "name=igny8_marketing" +docker logs igny8_marketing --tail 20 +``` + +### Update Marketing Site +```bash +cd /data/app/igny8/frontend +docker build -t igny8-marketing:latest -f Dockerfile.marketing . +cd /data/app/igny8 +docker compose -f docker-compose.app.yml -p igny8-app restart igny8_marketing +``` + +### Test Connectivity +```bash +# Direct container access +curl http://localhost:8022/marketing.html + +# Through Caddy (production) +curl https://igny8.com/marketing.html +``` + +--- + +## Migration Complete + +✅ **Old manual process is deprecated** +✅ **New containerized process is active** +✅ **Site is fully operational** + +The marketing site is now: +- Containerized +- Version controlled (Docker images) +- Automatically deployed +- Easy to rollback +- Production-ready + +--- + +## Next Steps (Optional) + +1. **Set up CI/CD** - Automate builds on git push +2. **Add health checks** - Monitor container health +3. **Set up monitoring** - Track container metrics +4. **Create backup strategy** - Tag images before updates + +--- + +**See Also:** +- [Marketing Deployment Guide](./MARKETING_DEPLOYMENT.md) +- [Deployment Architecture](./DEPLOYMENT_ARCHITECTURE.md) + diff --git a/docs/MARKETING_DEV_ENVIRONMENT.md b/docs/MARKETING_DEV_ENVIRONMENT.md new file mode 100644 index 00000000..25aee743 --- /dev/null +++ b/docs/MARKETING_DEV_ENVIRONMENT.md @@ -0,0 +1,160 @@ +# Marketing Development Environment with HMR + +**Status:** ✅ **ACTIVE** + +The marketing site now has a development environment with Hot Module Replacement (HMR) - just like the app dev environment! + +--- + +## 🚀 Quick Start + +### Current Setup +- **Dev Server:** `igny8_marketing_dev` (Vite with HMR) +- **Access:** `https://igny8.com` (routed through Caddy) +- **Direct Access:** `http://localhost:8023` +- **Port:** 5174 (internal), 8023 (external) + +### How It Works +1. **Volume Mount:** `/data/app/igny8/frontend` → `/app` (live file watching) +2. **HMR Enabled:** Changes to files/images update in real-time +3. **No Rebuild Needed:** Just edit files and see changes instantly! + +--- + +## 📝 Development Workflow + +### Making Changes +1. **Edit files** in `/data/app/igny8/frontend/src/marketing/` +2. **Edit images** in `/data/app/igny8/frontend/public/marketing/images/` +3. **See changes instantly** - HMR updates the browser automatically! + +### No Need To: +- ❌ Run `npm run build:marketing` +- ❌ Rebuild Docker image +- ❌ Restart container +- ❌ Copy files manually + +### Just: +- ✅ Edit files +- ✅ Save +- ✅ See changes in browser (HMR handles the rest!) + +--- + +## 🔄 Switching Between Dev and Production + +### Development Mode (Current) +**Caddyfile routes to:** `igny8_marketing_dev:5174` + +```caddyfile +igny8.com { + reverse_proxy igny8_marketing_dev:5174 { + # WebSocket support for HMR + header_up Connection {>Connection} + header_up Upgrade {>Upgrade} + } +} +``` + +### Production Mode +**Caddyfile routes to:** `igny8_marketing:8020` + +```caddyfile +igny8.com { + reverse_proxy igny8_marketing:8020 { + # Static production build + } +} +``` + +**To switch:** Edit `/var/lib/docker/volumes/portainer_data/_data/caddy/Caddyfile` and restart Caddy. + +--- + +## 🛠️ Container Management + +### Start Dev Server +```bash +cd /data/app/igny8 +docker compose -f docker-compose.app.yml -p igny8-app up -d igny8_marketing_dev +``` + +### View Logs +```bash +docker logs igny8_marketing_dev -f +``` + +### Restart Dev Server +```bash +docker compose -f docker-compose.app.yml -p igny8-app restart igny8_marketing_dev +``` + +### Stop Dev Server +```bash +docker compose -f docker-compose.app.yml -p igny8-app stop igny8_marketing_dev +``` + +--- + +## 📂 File Locations + +| Type | Location | +|------|----------| +| **Marketing Components** | `/data/app/igny8/frontend/src/marketing/` | +| **Marketing Pages** | `/data/app/igny8/frontend/src/marketing/pages/` | +| **Marketing Images** | `/data/app/igny8/frontend/public/marketing/images/` | +| **Marketing Styles** | `/data/app/igny8/frontend/src/marketing/styles/` | + +--- + +## ✅ Benefits + +1. **Real-time Updates** - Changes reflect immediately +2. **No Rebuilds** - Edit and save, that's it! +3. **Fast Development** - Same experience as app dev environment +4. **Image Updates** - Images in `public/marketing/images/` update instantly +5. **Component Updates** - React components hot-reload automatically + +--- + +## 🔍 Troubleshooting + +### Changes Not Appearing +1. Check container is running: `docker ps | grep igny8_marketing_dev` +2. Check logs: `docker logs igny8_marketing_dev` +3. Verify volume mount: Files should be in `/data/app/igny8/frontend/` + +### HMR Not Working +1. Check browser console for WebSocket errors +2. Verify Caddyfile has WebSocket headers +3. Restart Caddy: `docker compose restart caddy` + +### Port Conflicts +- Dev server uses port 5174 (internal), 8023 (external) +- If conflicts occur, change port in `docker-compose.app.yml` + +--- + +## 📊 Current Status + +✅ **Dev Server:** Running +✅ **HMR:** Enabled +✅ **Volume Mount:** Active +✅ **Caddy Routing:** Configured +✅ **WebSocket Support:** Enabled + +--- + +## 🎯 Next Steps + +When ready for production: +1. Build production image: `docker build -t igny8-marketing:latest -f Dockerfile.marketing .` +2. Update Caddyfile to route to `igny8_marketing:8020` +3. Restart Caddy: `docker compose restart caddy` + +--- + +**See Also:** +- [Marketing Deployment Guide](./MARKETING_DEPLOYMENT.md) +- [Deployment Architecture](./DEPLOYMENT_ARCHITECTURE.md) + diff --git a/frontend/Dockerfile.marketing.dev b/frontend/Dockerfile.marketing.dev new file mode 100644 index 00000000..28136f72 --- /dev/null +++ b/frontend/Dockerfile.marketing.dev @@ -0,0 +1,20 @@ +# Marketing Development Dockerfile - Vite Dev Server with Hot Reload +FROM node:18-alpine + +WORKDIR /app + +# Copy package files +COPY package*.json ./ + +# Install dependencies +RUN npm install + +# Copy source code (will be mounted as volume, but needed for initial setup) +COPY . . + +# Expose Vite dev server port +EXPOSE 5174 + +# Start Vite dev server for marketing site +CMD ["npm", "run", "dev:marketing"] + diff --git a/frontend/node_modules/.vite/deps/_metadata.json b/frontend/node_modules/.vite/deps/_metadata.json index b0163036..4eeefe02 100644 --- a/frontend/node_modules/.vite/deps/_metadata.json +++ b/frontend/node_modules/.vite/deps/_metadata.json @@ -7,91 +7,91 @@ "clsx": { "src": "../../clsx/dist/clsx.mjs", "file": "clsx.js", - "fileHash": "612eb115", + "fileHash": "335e755e", "needsInterop": false }, "tailwind-merge": { "src": "../../tailwind-merge/dist/bundle-mjs.mjs", "file": "tailwind-merge.js", - "fileHash": "fae71fe8", + "fileHash": "ba411a67", "needsInterop": false }, "zustand": { "src": "../../zustand/esm/index.mjs", "file": "zustand.js", - "fileHash": "66812e04", + "fileHash": "01c7a42f", "needsInterop": false }, "apexcharts": { "src": "../../apexcharts/dist/apexcharts.esm.js", "file": "apexcharts.js", - "fileHash": "abf4aac5", + "fileHash": "95f9f7ed", "needsInterop": false }, "react-apexcharts": { "src": "../../react-apexcharts/dist/react-apexcharts.min.js", "file": "react-apexcharts.js", - "fileHash": "f52d726c", + "fileHash": "c6ad3fcf", "needsInterop": true }, "react": { "src": "../../react/index.js", "file": "react.js", - "fileHash": "7e35b58a", + "fileHash": "552f8751", "needsInterop": true }, "react-dom": { "src": "../../react-dom/index.js", "file": "react-dom.js", - "fileHash": "dfafaab1", + "fileHash": "51ab02f8", "needsInterop": true }, "react/jsx-dev-runtime": { "src": "../../react/jsx-dev-runtime.js", "file": "react_jsx-dev-runtime.js", - "fileHash": "b406d5db", + "fileHash": "3eac29f9", "needsInterop": true }, "react/jsx-runtime": { "src": "../../react/jsx-runtime.js", "file": "react_jsx-runtime.js", - "fileHash": "dd354b52", + "fileHash": "f475eb11", "needsInterop": true }, "@heroicons/react/24/outline": { "src": "../../@heroicons/react/24/outline/esm/index.js", "file": "@heroicons_react_24_outline.js", - "fileHash": "1527a1b7", + "fileHash": "1384c9cc", "needsInterop": false }, "react-dom/client": { "src": "../../react-dom/client.js", "file": "react-dom_client.js", - "fileHash": "f8ebcf66", + "fileHash": "7f1e08f7", "needsInterop": true }, "react-helmet-async": { "src": "../../react-helmet-async/lib/index.esm.js", "file": "react-helmet-async.js", - "fileHash": "67572141", + "fileHash": "dbe6d36e", "needsInterop": false }, "react-router": { "src": "../../react-router/dist/development/index.mjs", "file": "react-router.js", - "fileHash": "10c21415", + "fileHash": "87eba0c4", "needsInterop": false }, "react-router-dom": { "src": "../../react-router-dom/dist/index.mjs", "file": "react-router-dom.js", - "fileHash": "c3e017b5", + "fileHash": "719445bb", "needsInterop": false }, "zustand/middleware": { "src": "../../zustand/esm/middleware.mjs", "file": "zustand_middleware.js", - "fileHash": "ba8cc813", + "fileHash": "4c1c6b84", "needsInterop": false } }, diff --git a/frontend/package.json b/frontend/package.json index 0c14e135..239ee6e0 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -5,6 +5,7 @@ "type": "module", "scripts": { "dev": "vite", + "dev:marketing": "vite --host 0.0.0.0 --port 5174 --force marketing.html", "build": "vite build", "build:marketing": "vite build --mode marketing", "build:check": "tsc -b && vite build", diff --git a/frontend/src/marketing/images/hero-dashboard.png b/frontend/src/marketing/images/hero-dashboard.png index 126cb151..5700a337 100644 Binary files a/frontend/src/marketing/images/hero-dashboard.png and b/frontend/src/marketing/images/hero-dashboard.png differ diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index c065573f..4eb43d33 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -83,10 +83,11 @@ export default defineConfig(({ mode }) => { ...(isDev && { server: { host: "0.0.0.0", // Allow external connections (for Docker) - port: 5173, - strictPort: false, // Allow port fallback if 5173 is busy + port: isMarketingBuild ? 5174 : 5173, // Different port for marketing dev + strictPort: false, // Allow port fallback if port is busy allowedHosts: [ "app.igny8.com", + "igny8.com", "localhost", "127.0.0.1", ], @@ -95,11 +96,11 @@ export default defineConfig(({ mode }) => { }, hmr: { // Behind reverse proxy - use same origin strategy - // Client connects via public domain (app.igny8.com) on default HTTPS port (443) - // Caddy automatically proxies WebSocket upgrades from 443 to 5173 + // Client connects via public domain on default HTTPS port (443) + // Caddy automatically proxies WebSocket upgrades from 443 to dev port protocol: "wss", // Don't specify host/port - Vite will use same origin as page - // This ensures client connects to wss://app.igny8.com (port 443 implicit) + // This ensures client connects to wss://domain.com (port 443 implicit) }, // Increase timeout for slow connections and dependency pre-bundling timeout: 120000, // 120 seconds (2 minutes) to match Caddy timeout