diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md
new file mode 100644
index 0000000..24f3f5a
--- /dev/null
+++ b/DEPLOYMENT.md
@@ -0,0 +1,133 @@
+# Cloud Run Deployment Guide
+
+This guide will help you deploy the webpage replicator application to Google Cloud Run.
+
+## Prerequisites
+
+1. **Google Cloud Account**: You need a Google Cloud account with billing enabled
+2. **Google Cloud CLI**: Install the `gcloud` CLI tool
+3. **Docker**: Ensure Docker is installed and running (for local testing)
+4. **Project Setup**: Create a Google Cloud project
+
+## Quick Deployment
+
+### Option 1: Using the deployment script (Recommended)
+
+1. **Update the deployment script**:
+ ```bash
+ # Edit deploy.sh and replace 'your-project-id' with your actual project ID
+ nano deploy.sh
+ ```
+
+2. **Make the script executable and run it**:
+ ```bash
+ chmod +x deploy.sh
+ ./deploy.sh
+ ```
+
+### Option 2: Manual deployment using YAML files
+
+1. **Set your project ID**:
+ ```bash
+ export PROJECT_ID="your-project-id"
+ gcloud config set project $PROJECT_ID
+ ```
+
+2. **Enable required APIs**:
+ ```bash
+ gcloud services enable cloudbuild.googleapis.com
+ gcloud services enable run.googleapis.com
+ ```
+
+3. **Update YAML files**:
+ - Replace `PROJECT_ID` in both `frontend/cloudrun.yaml` and `backend/cloudrun.yaml` with your actual project ID
+
+4. **Build and deploy backend**:
+ ```bash
+ cd backend
+ gcloud builds submit --tag gcr.io/$PROJECT_ID/webpage-replicator-backend
+ gcloud run services replace cloudrun.yaml --region=us-central1
+ cd ..
+ ```
+
+5. **Build and deploy frontend**:
+ ```bash
+ cd frontend
+ gcloud builds submit --tag gcr.io/$PROJECT_ID/webpage-replicator-frontend
+ gcloud run services replace cloudrun.yaml --region=us-central1
+ cd ..
+ ```
+
+## Configuration
+
+### Backend Configuration
+
+The backend service may require environment variables for API keys and other configuration. You can set these using:
+
+```bash
+gcloud run services update webpage-replicator-backend \
+ --region=us-central1 \
+ --set-env-vars="GEMINI_API_KEY=your-api-key-here"
+```
+
+Or use Google Secret Manager for sensitive data:
+
+```bash
+# Create a secret
+gcloud secrets create gemini-api-key --data-file=api-key.txt
+
+# Update the service to use the secret
+gcloud run services update webpage-replicator-backend \
+ --region=us-central1 \
+ --set-secrets="GEMINI_API_KEY=gemini-api-key:latest"
+```
+
+### Frontend Configuration
+
+If your frontend needs to communicate with the backend, update any API endpoint URLs in your frontend code to use the deployed backend URL.
+
+## Monitoring and Logs
+
+- **View logs**: `gcloud run logs read webpage-replicator-backend --region=us-central1`
+- **Monitor metrics**: Visit the Cloud Console > Cloud Run to view metrics and performance
+
+## Costs
+
+Cloud Run pricing is based on:
+- CPU and memory allocation
+- Number of requests
+- Request duration
+
+The current configuration uses:
+- **Frontend**: 1 vCPU, 512Mi memory
+- **Backend**: 2 vCPU, 1Gi memory
+
+Both services scale to zero when not in use, so you only pay for actual usage.
+
+## Troubleshooting
+
+### Common Issues
+
+1. **Build failures**: Check that all dependencies are properly listed in `package.json`
+2. **Port issues**: Ensure your application listens on the port specified in the `PORT` environment variable
+3. **Health check failures**: Make sure your backend has a `/health` endpoint or update the health check path
+
+### Useful Commands
+
+```bash
+# View service details
+gcloud run services describe webpage-replicator-backend --region=us-central1
+
+# View recent deployments
+gcloud run revisions list --service=webpage-replicator-backend --region=us-central1
+
+# Delete a service
+gcloud run services delete webpage-replicator-backend --region=us-central1
+```
+
+## Security Considerations
+
+- Both services are currently configured to allow unauthenticated access
+- For production, consider implementing authentication
+- Use IAM roles to control access to your services
+- Store sensitive configuration in Google Secret Manager
\ No newline at end of file
diff --git a/backend/.dockerignore b/backend/.dockerignore
new file mode 100644
index 0000000..cb0aa94
--- /dev/null
+++ b/backend/.dockerignore
@@ -0,0 +1,14 @@
+node_modules
+npm-debug.log
+.git
+.gitignore
+README.md
+.env
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
+.DS_Store
+*.log
+uploads/
+temp/
\ No newline at end of file
diff --git a/backend/Dockerfile b/backend/Dockerfile
new file mode 100644
index 0000000..242a971
--- /dev/null
+++ b/backend/Dockerfile
@@ -0,0 +1,20 @@
+# Use Node.js official image
+FROM node:18-alpine
+
+# Set working directory
+WORKDIR /app
+
+# Copy package files
+COPY package*.json ./
+
+# Install dependencies
+RUN npm ci --only=production
+
+# Copy application files
+COPY . .
+
+# Expose port (assuming Express server runs on port 3001 or process.env.PORT)
+EXPOSE 3001
+
+# Start the application
+CMD ["npm", "start"]
\ No newline at end of file
diff --git a/backend/cloudrun.yaml b/backend/cloudrun.yaml
new file mode 100644
index 0000000..c90ccf5
--- /dev/null
+++ b/backend/cloudrun.yaml
@@ -0,0 +1,57 @@
+apiVersion: serving.knative.dev/v1
+kind: Service
+metadata:
+ name: webpage-replicator-backend
+ annotations:
+ run.googleapis.com/ingress: all
+ run.googleapis.com/ingress-status: all
+spec:
+ template:
+ metadata:
+ annotations:
+ autoscaling.knative.dev/maxScale: "100"
+ run.googleapis.com/cpu-throttling: "false"
+ run.googleapis.com/execution-environment: gen2
+ spec:
+ containerConcurrency: 80
+ timeoutSeconds: 300
+ containers:
+ - image: gcr.io/PROJECT_ID/webpage-replicator-backend:latest
+ ports:
+ - name: http1
+ containerPort: 3001
+ env:
+ - name: PORT
+ value: "3001"
+ - name: NODE_ENV
+ value: "production"
+ # Add your environment variables here
+ # - name: GEMINI_API_KEY
+ # valueFrom:
+ # secretKeyRef:
+ # name: gemini-secrets
+ # key: api-key
+ resources:
+ limits:
+ cpu: 2000m
+ memory: 1Gi
+ requests:
+ cpu: 200m
+ memory: 256Mi
+ livenessProbe:
+ httpGet:
+ path: /health
+ port: 3001
+ initialDelaySeconds: 30
+ periodSeconds: 30
+ failureThreshold: 3
+ readinessProbe:
+ httpGet:
+ path: /health
+ port: 3001
+ initialDelaySeconds: 5
+ periodSeconds: 10
+ failureThreshold: 3
+ traffic:
+ - percent: 100
+ latestRevision: true
\ No newline at end of file
diff --git a/backend/gcs-service.js b/backend/gcs-service.js
new file mode 100644
index 0000000..a107bf1
--- /dev/null
+++ b/backend/gcs-service.js
@@ -0,0 +1,157 @@
+import { Storage } from '@google-cloud/storage';
+
+class GCSService {
+ constructor() {
+ this.storage = new Storage();
+ this.bucketName = process.env.GCS_BUCKET_NAME;
+
+ if (!this.bucketName) {
+ throw new Error('GCS_BUCKET_NAME environment variable is required');
+ }
+
+ this.bucket = this.storage.bucket(this.bucketName);
+ }
+
+ /**
+ * Upload a file to GCS
+ * @param {string} fileName - The name/path of the file in the bucket
+ * @param {Buffer} fileBuffer - The file content as a buffer
+ * @param {string} contentType - The MIME type of the file
+ * @returns {Promise
tag
+ const bodyCloseIndex = html.lastIndexOf('');
+ if (bodyCloseIndex !== -1) {
+ html = html.slice(0, bodyCloseIndex) + clickthroughIntegration + html.slice(bodyCloseIndex);
+ console.log('Clickthrough integration added successfully');
+ } else {
+ // If no tag, append to end
+ html += clickthroughIntegration;
+ console.log('Clickthrough integration appended to end (no tag found)');
+ }
+
+ return html;
+
+ } catch (error) {
+ console.error('Error adding Clickthrough integration:', error);
+ // Return original HTML if processing fails
+ return html;
+ }
+}
+
+// Endpoint to upload screenshot and generate webpage
+app.post('/api/generate-page', upload.single('screenshot'), async (req, res) => {
+ try {
+ if (!req.file) {
+ return res.status(400).json({ error: 'No screenshot uploaded' });
+ }
+
+ const pageId = uuidv4();
+ const screenshotBuffer = req.file.buffer;
+
+ // Get Clickthrough parameters from form data
+ const clickthroughId = req.body.clickthroughId;
+ const clusterId = req.body.clusterId;
+
+ console.log('Received parameters:', {
+ clickthroughId,
+ clusterId,
+ hasFile: !!req.file,
+ bodyKeys: Object.keys(req.body)
+ });
+
+ // Convert image to base64 for Gemini
+ const base64Image = screenshotBuffer.toString('base64');
+ const mimeType = req.file.mimetype;
+
+ // Generate HTML/CSS/JS using Gemini - CLEAN GENERATION WITHOUT CLICKTHROUGH
+
+ // Universal instructions that always apply
+ const imageHandlingInstructions = `
+
+ CRITICAL IMAGE HANDLING INSTRUCTIONS (ALWAYS APPLY):
+ - DO NOT include any tags or image references from the screenshot
+ - DO NOT attempt to replicate logos, photos, graphics, or any visual images
+ - REPLACE image areas with appropriate styled elements:
+ * For logos: Use styled text/typography or CSS-based geometric shapes
+ * For decorative images: Use CSS backgrounds, gradients, or colored divs
+ * For photos: Use placeholder colored backgrounds or CSS patterns
+ * For icons: Use CSS symbols, Unicode characters, or styled elements
+ - Focus on creating a clean, functional page without broken image links
+ - Use colors, typography, and CSS styling to maintain visual hierarchy instead of images
+ `;
+
+ let clickthroughInstructions = '';
+ if (clickthroughId && clusterId) {
+ clickthroughInstructions = `
+
+ IMPORTANT: Do NOT include any terms and conditions, privacy policy checkboxes, or legal acceptance elements in your generated HTML. These will be added automatically during post-processing.
+ `;
+ }
+
+ const prompt = `
+ Analyze this screenshot of a webpage and generate complete HTML, CSS, and JavaScript code to replicate it as closely as possible.
+
+ Critical Requirements for Accurate Replication:
+
+ TYPOGRAPHY & FONTS:
+ - Match exact font families, sizes, and weights
+ - Replicate line-height, letter-spacing, and text alignment
+ - Preserve heading hierarchy and text formatting
+ - Ensure proper font loading and fallbacks
+
+ PAGE FORMATTING & LAYOUT:
+ - Create pixel-perfect replica of spacing, margins, and padding
+ - Match exact element positioning and alignment
+ - Preserve proportions and visual hierarchy
+ - Implement responsive design with proper breakpoints
+
+ VISUAL DETAILS:
+ - Match colors exactly (backgrounds, text, borders)
+ - Replicate shadows, gradients, and visual effects
+ - Preserve border radius, styling, and decorative elements
+ - Maintain consistent spacing between all elements
+
+ TECHNICAL REQUIREMENTS:
+ - Use modern CSS (flexbox, grid) for accurate layout
+ - Include all interactive elements and form styling
+ - Implement proper semantic HTML structure
+ - Add inline CSS and JavaScript in single HTML file
+ - Ensure full functionality with form validation and interactions
+ ${imageHandlingInstructions}
+ ${clickthroughInstructions}
+
+ Focus on maintaining the exact visual appearance and formatting integrity of the original design.
+
+ IMPORTANT: Return ONLY the complete HTML code with embedded CSS and JavaScript. Do not use markdown code blocks, backticks, or any formatting - just return the raw HTML code directly.
+ `;
+
+ const response = await genAI.models.generateContent({
+ model: process.env.GEMINI_MODEL || "gemini-2.5-flash",
+ contents: [
+ {
+ role: "user",
+ parts: [
+ { text: prompt },
+ {
+ inlineData: {
+ data: base64Image,
+ mimeType: mimeType
+ }
+ }
+ ]
+ }
+ ]
+ });
+
+ let generatedHTML = response.text;
+
+ // Clean up markdown code block formatting if present
+ generatedHTML = generatedHTML
+ .replace(/^```html\s*/i, '') // Remove opening ```html
+ .replace(/^```\s*/gm, '') // Remove any other opening ```
+ .replace(/\s*```$/gm, '') // Remove closing ```
+ .replace(/```html/gi, '') // Remove any remaining ```html
+ .replace(/```/g, '') // Remove any remaining ```
+ .trim();
+
+ // POST-PROCESS: Clean up any image references (always apply)
+ generatedHTML = removeImageReferences(generatedHTML);
+
+ // POST-PROCESS: Add Clickthrough integration if parameters provided
+ if (clickthroughId && clusterId) {
+ generatedHTML = addClickthroughToHTML(generatedHTML, clickthroughId, clusterId);
+ }
+
+ // Save generated files to GCS
+ const htmlFileName = `pages/${pageId}/index.html`;
+ const screenshotFileName = `pages/${pageId}/original.png`;
+
+ // Save the HTML file to GCS
+ const htmlUrl = await gcsService.uploadFile(htmlFileName, Buffer.from(generatedHTML, 'utf8'), 'text/html');
+
+ // Save the original screenshot to GCS
+ const screenshotUrl = await gcsService.uploadFile(screenshotFileName, screenshotBuffer, 'image/png');
+
+ res.json({
+ success: true,
+ pageId: pageId,
+ url: htmlUrl,
+ previewUrl: htmlUrl,
+ screenshotUrl: screenshotUrl
+ });
+
+ } catch (error) {
+ console.error('Error generating page:', error);
+ res.status(500).json({
+ error: 'Failed to generate page',
+ details: error.message
+ });
+ }
+});
+
+// Endpoint to compare generated page with original screenshot
+app.post('/api/compare-page/:pageId', async (req, res) => {
+ try {
+ const { pageId } = req.params;
+
+ // Download files from GCS
+ const screenshotFileName = `pages/${pageId}/original.png`;
+ const htmlFileName = `pages/${pageId}/index.html`;
+
+ const originalBuffer = await gcsService.downloadFile(screenshotFileName);
+ const base64Original = originalBuffer.toString('base64');
+
+ // Download and read the HTML content
+ const htmlBuffer = await gcsService.downloadFile(htmlFileName);
+ const htmlContent = htmlBuffer.toString('utf8');
+
+ const prompt = `
+ Compare this original screenshot with the HTML code that was generated to replicate it.
+
+ Analyze and rate the similarity on a scale of 1-10, paying special attention to:
+
+ 1. LAYOUT ACCURACY:
+ - Overall page structure and component arrangement
+ - Spacing, margins, and padding consistency
+ - Grid/flexbox alignment and distribution
+ - Responsive design elements
+
+ 2. TYPOGRAPHY & FONT FORMATTING:
+ - Font family, size, and weight matching
+ - Line height and letter spacing
+ - Text alignment and justification
+ - Heading hierarchy and consistency
+ - Text color and contrast accuracy
+
+ 3. COLOR MATCHING:
+ - Background colors and gradients
+ - Text colors and readability
+ - Button and interactive element colors
+ - Border colors and styling
+
+ 4. ELEMENT POSITIONING:
+ - Precise placement of all UI elements
+ - Alignment of buttons, inputs, and forms
+ - Icon and image positioning
+ - Consistent spacing between elements
+
+ 5. PAGE FORMATTING:
+ - Overall page dimensions and proportions
+ - Section breaks and content organization
+ - Visual hierarchy maintenance
+ - Brand consistency and styling
+
+ 6. DETAILED FORMATTING:
+ - Border radius and shadows
+ - Input field styling and placeholder text
+ - Button hover states and interactions
+ - Form validation styling
+
+ HTML Code Analysis:
+ ${htmlContent.substring(0, 8000)} // Extended for better analysis
+
+ Provide a JSON response with detailed scoring:
+ {
+ "similarity_score": number (1-10),
+ "layout_score": number (1-10),
+ "color_score": number (1-10),
+ "typography_score": number (1-10),
+ "positioning_score": number (1-10),
+ "formatting_score": number (1-10),
+ "font_accuracy_score": number (1-10),
+ "feedback": "detailed feedback focusing on typography, formatting, and layout precision",
+ "font_issues": ["specific font/typography problems"],
+ "formatting_issues": ["specific page formatting problems"],
+ "improvements": ["detailed suggestions for typography and formatting fixes"]
+ }
+ `;
+
+ const response = await genAI.models.generateContent({
+ model: process.env.GEMINI_MODEL || "gemini-2.5-flash",
+ contents: [
+ {
+ role: "user",
+ parts: [
+ { text: prompt },
+ {
+ inlineData: {
+ data: base64Original,
+ mimeType: 'image/png'
+ }
+ }
+ ]
+ }
+ ]
+ });
+
+ const comparison = JSON.parse(response.text);
+
+ res.json({
+ success: true,
+ pageId: pageId,
+ comparison: comparison
+ });
+
+ } catch (error) {
+ console.error('Error comparing page:', error);
+ res.status(500).json({
+ error: 'Failed to compare page',
+ details: error.message
+ });
+ }
+});
+
+// Endpoint to list all generated pages
+app.get('/api/pages', async (req, res) => {
+ try {
+ const pages = await gcsService.listFiles('pages/');
+
+ // Group files by page ID and create page objects
+ const pageMap = new Map();
+
+ pages.forEach(file => {
+ // Extract pageId from path like 'pages/uuid/index.html'
+ const pathParts = file.name.split('/');
+ if (pathParts.length >= 3 && pathParts[0] === 'pages') {
+ const pageId = pathParts[1];
+ if (!pageMap.has(pageId)) {
+ pageMap.set(pageId, {
+ id: pageId,
+ createdAt: file.created
+ });
+ }
+
+ // Add URL for HTML files
+ if (pathParts[2] === 'index.html') {
+ pageMap.get(pageId).url = file.publicUrl;
+ pageMap.get(pageId).previewUrl = file.publicUrl;
+ }
+ // Add screenshot URL for PNG files
+ if (pathParts[2] === 'original.png') {
+ pageMap.get(pageId).screenshotUrl = file.publicUrl;
+ }
+ }
+ });
+
+ const pageList = Array.from(pageMap.values())
+ .filter(page => page.url) // Only include pages with HTML files
+ .sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt));
+
+ res.json({
+ success: true,
+ pages: pageList
+ });
+
+ } catch (error) {
+ console.error('Error listing pages:', error);
+ res.status(500).json({
+ error: 'Failed to list pages',
+ details: error.message
+ });
+ }
+});
+
+// Health check endpoint
+app.get('/api/health', (req, res) => {
+ res.json({ status: 'ok', service: 'webpage-replicator-backend' });
+});
+
+// Test Clickthrough integration endpoint
+app.post('/api/test-clickthrough', (req, res) => {
+ const testHTML = `
+
+