diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..37ac115 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,7 @@ +node_modules +.git +.gitignore +README.md +Dockerfile +.dockerignore +*.log \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c423d90 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,42 @@ +# Use official Node.js runtime as base image +FROM node:18-alpine + +# Install Chromium and dependencies for Puppeteer +RUN apk add --no-cache \ + chromium \ + nss \ + freetype \ + freetype-dev \ + harfbuzz \ + ca-certificates \ + ttf-freefont + +# Tell Puppeteer to skip installing Chromium. We'll be using the installed package. +ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true \ + PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser + +# Set working directory +WORKDIR /app + +# Copy package files +COPY package*.json ./ + +# Install dependencies +RUN npm ci --only=production + +# Copy application code +COPY . . + +# Create non-root user +RUN addgroup -g 1001 -S nodejs +RUN adduser -S nextjs -u 1001 + +# Change ownership of the app directory +RUN chown -R nextjs:nodejs /app +USER nextjs + +# Expose port +EXPOSE 3000 + +# Start the application +CMD ["npm", "start"] \ No newline at end of file diff --git a/server.js b/server.js index c6c1cc1..0a0c638 100644 --- a/server.js +++ b/server.js @@ -20,7 +20,18 @@ async function scrapePDGARating(pdgaNumber) { return cached.data; } - const browser = await puppeteer.launch({ headless: true }); + const browser = await puppeteer.launch({ + headless: "new", + args: [ + '--no-sandbox', + '--disable-setuid-sandbox', + '--disable-dev-shm-usage', + '--disable-accelerated-2d-canvas', + '--no-first-run', + '--no-zygote', + '--disable-gpu' + ] + }); const page = await browser.newPage(); try { @@ -37,29 +48,14 @@ async function scrapePDGARating(pdgaNumber) { for (const el of elements) { const text = el.innerText || el.textContent; if (text.includes('Current Rating:')) { - console.log('Found rating text:', text); const ratingMatch = text.match(/Current Rating:\s*(\d+)/); - // Try different patterns for rating change - const changePatterns = [ - /\[(\+\d+)\]/, - /\[(\-\d+)\]/, - /(\+\d+)/, - /(\-\d+)/ - ]; - - let change = null; - for (const pattern of changePatterns) { - const match = text.match(pattern); - if (match) { - change = match[1]; - break; - } - } + // Look for rating change pattern: "Current Rating: 911 +6 (as of..." + const changeMatch = text.match(/Current Rating:\s*\d+\s+([+\-]\d+)\s+\(as of/); return { rating: ratingMatch ? ratingMatch[1] : null, - change: change + change: changeMatch ? changeMatch[1] : null }; } } @@ -318,7 +314,18 @@ app.get('/api/ratings', async (req, res) => { app.post('/api/predicted-rating/:pdgaNumber', async (req, res) => { try { const { pdgaNumber } = req.params; - const browser = await puppeteer.launch({ headless: true }); + const browser = await puppeteer.launch({ + headless: "new", + args: [ + '--no-sandbox', + '--disable-setuid-sandbox', + '--disable-dev-shm-usage', + '--disable-accelerated-2d-canvas', + '--no-first-run', + '--no-zygote', + '--disable-gpu' + ] + }); console.log(`Calculating predicted rating for PDGA ${pdgaNumber}...`); const predictedRating = await getPredictedRating(browser, pdgaNumber);