diff --git a/Dockerfile b/Dockerfile index 4035e2a..e936228 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,15 +1,21 @@ -# Stage 1: Build client -FROM node:20-alpine AS client-builder +# Build stage +FROM node:20-alpine AS builder -WORKDIR /app/client +WORKDIR /app -COPY client/package*.json ./ +# Copy package files +COPY package*.json ./ + +# Install all dependencies (including client devDependencies for build) RUN npm install -COPY client/ ./ +# Copy client source for build +COPY client/ ./client/ + +# Build the client RUN npm run build -# Stage 2: Production server +# Production stage FROM node:20-alpine # Install system dependencies for node-canvas and sharp @@ -26,15 +32,15 @@ RUN apk add --no-cache \ WORKDIR /app -# Copy server package files and install -COPY server/package*.json ./server/ -RUN cd server && npm install --production +# Copy package files and install production dependencies only +COPY package*.json ./ +RUN npm install --production # Copy server source COPY server/ ./server/ # Copy built client from builder -COPY --from=client-builder /app/client/dist ./server/dist +COPY --from=builder /app/client/dist ./server/dist # Create data directories RUN mkdir -p /app/server/uploads /app/server/exports diff --git a/client/package.json b/client/package.json deleted file mode 100644 index 9f4b772..0000000 --- a/client/package.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "name": "client", - "private": true, - "version": "0.0.0", - "type": "module", - "scripts": { - "dev": "vite", - "build": "vite build", - "lint": "eslint .", - "preview": "vite preview" - }, - "dependencies": { - "react": "^19.2.5", - "react-dom": "^19.2.5", - "react-konva": "^18.2.10", - "konva": "^9.3.18", - "use-image": "^1.1.1", - "@xenova/transformers": "^2.17.2", - "react-filerobot-image-editor": "^4.8.1" - }, - "devDependencies": { - "@eslint/js": "^9.39.4", - "@types/react": "^19.2.14", - "@types/react-dom": "^19.2.3", - "@vitejs/plugin-react": "^6.0.1", - "eslint": "^9.39.4", - "eslint-plugin-react-hooks": "^7.1.1", - "eslint-plugin-react-refresh": "^0.5.2", - "globals": "^17.5.0", - "vite": "^8.0.9", - "vite-plugin-pwa": "^0.20.5", - "workbox-window": "^7.1.0" - } -} diff --git a/client/vite.config.js b/client/vite.config.js index c487481..51e54d1 100644 --- a/client/vite.config.js +++ b/client/vite.config.js @@ -154,4 +154,7 @@ export default defineConfig({ }, }, }, + build: { + outDir: 'dist', + }, }); diff --git a/package.json b/package.json index 8fc60a8..9a2e3d9 100644 --- a/package.json +++ b/package.json @@ -4,15 +4,45 @@ "description": "T-shirt customization editor with background removal, stickers, text, and export", "private": true, "type": "module", + "main": "server/index.js", "scripts": { - "postinstall": "cd client && npm install && cd ../server && npm install", "dev": "concurrently \"npm run dev:client\" \"npm run dev:server\"", - "dev:client": "cd client && npm run dev", - "dev:server": "cd server && npm run dev", - "build": "cd client && npm run build", - "start": "node server/index.js" + "dev:client": "cd client && vite", + "dev:server": "node --watch server/index.js", + "build": "cd client && vite build", + "start": "node server/index.js", + "install:all": "npm install && cd client && npm install" + }, + "dependencies": { + "canvas": "^2.11.2", + "cors": "^2.8.5", + "express": "^4.18.2", + "multer": "^1.4.5-lts.1", + "sharp": "^0.33.2", + "uuid": "^9.0.1" }, "devDependencies": { - "concurrently": "^8.2.0" + "@eslint/js": "^9.39.4", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.1", + "concurrently": "^8.2.0", + "eslint": "^9.39.4", + "eslint-plugin-react-hooks": "^7.1.1", + "eslint-plugin-react-refresh": "^0.5.2", + "globals": "^17.5.0", + "vite": "^8.0.9", + "vite-plugin-pwa": "^0.20.5", + "workbox-window": "^7.1.0", + "react": "^19.2.5", + "react-dom": "^19.2.5", + "react-konva": "^18.2.10", + "konva": "^9.3.18", + "use-image": "^1.1.1", + "@xenova/transformers": "^2.17.2", + "react-filerobot-image-editor": "^4.8.1" + }, + "engines": { + "node": ">=20.0.0" } } diff --git a/server/index.js b/server/index.js index 16881b3..0a7e85a 100644 --- a/server/index.js +++ b/server/index.js @@ -30,6 +30,21 @@ app.use(express.urlencoded({ extended: true, limit: '50mb' })); app.use('/uploads', express.static(uploadsDir)); app.use('/exports', express.static(exportsDir)); +// Serve built client static files +const clientDistDir = join(__dirname, 'dist'); +if (existsSync(clientDistDir)) { + app.use(express.static(clientDistDir)); + + // Serve index.html for all non-API routes (SPA routing) + app.get('*', (req, res, next) => { + if (!req.path.startsWith('/api') && !req.path.startsWith('/uploads') && !req.path.startsWith('/exports')) { + res.sendFile(join(clientDistDir, 'index.html')); + } else { + next(); + } + }); +} + // Configure multer for image uploads const storage = multer.diskStorage({ destination: (req, file, cb) => { diff --git a/server/package.json b/server/package.json deleted file mode 100644 index 79fca87..0000000 --- a/server/package.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "apparel-designer-server", - "version": "1.0.0", - "type": "module", - "main": "index.js", - "scripts": { - "dev": "DYLD_INSERT_LIBRARIES='' node --watch index.js", - "start": "node index.js" - }, - "dependencies": { - "express": "^4.18.2", - "cors": "^2.8.5", - "multer": "^1.4.5-lts.1", - "sharp": "^0.33.2", - "uuid": "^9.0.1", - "canvas": "^2.11.2" - } -}