Compare commits
7 commits
a50b9dfa91
...
09f054b888
| Author | SHA1 | Date | |
|---|---|---|---|
| 09f054b888 | |||
| cdd36afd33 | |||
| 46b32d6aa7 | |||
| 7c2a58c894 | |||
| e1911bf5e8 | |||
|
|
fb133dff6c | ||
|
|
ff1330868e |
4 changed files with 96 additions and 85 deletions
|
|
@ -1,42 +0,0 @@
|
||||||
name: Build and Push Docker Image
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
paths:
|
|
||||||
- '**/*' # Триггер при любом изменении репозитория
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-and-push:
|
|
||||||
runs-on: docker
|
|
||||||
|
|
||||||
container:
|
|
||||||
image: docker:24.0.1
|
|
||||||
|
|
||||||
steps:
|
|
||||||
# Установка Docker CLI (если не в базовом образе)
|
|
||||||
- name: Setup Docker CLI
|
|
||||||
run: |
|
|
||||||
apk add --no-cache docker-cli
|
|
||||||
|
|
||||||
# Авторизация в Docker Hub - токен необходимо добавить в Secrets
|
|
||||||
- name: Login to Docker Hub
|
|
||||||
env:
|
|
||||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
|
||||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
|
||||||
run: |
|
|
||||||
echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
|
|
||||||
|
|
||||||
# Клонирование исходников — встроено в Forgejo Actions
|
|
||||||
|
|
||||||
# Сборка Docker образа
|
|
||||||
- name: Build Docker Image
|
|
||||||
run: |
|
|
||||||
docker build -t ${DOCKER_USERNAME}/playwright-domain-scanner:latest .
|
|
||||||
|
|
||||||
# Push образ на Docker Hub
|
|
||||||
- name: Push Docker Image
|
|
||||||
run: |
|
|
||||||
docker push ${DOCKER_USERNAME}/playwright-domain-scanner:latest
|
|
||||||
|
|
||||||
28
.github/workflows/docker-build-push.yaml
vendored
Normal file
28
.github/workflows/docker-build-push.yaml
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
name: Build and Push Docker Image
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-push:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
environment: dockerhub
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout the repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Log in to Docker Hub
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Build and push Docker image
|
||||||
|
uses: docker/build-push-action@v4
|
||||||
|
with:
|
||||||
|
push: true
|
||||||
|
tags: ${{ secrets.DOCKER_USERNAME }}/gekata:latest
|
||||||
|
|
||||||
74
Dockerfile
74
Dockerfile
|
|
@ -1,46 +1,48 @@
|
||||||
# Use official Node.js LTS base image
|
# Base minimal Debian
|
||||||
FROM node:20-slim
|
FROM debian:bookworm-slim
|
||||||
|
|
||||||
# Install dependencies for running Chromium
|
# Prevent tzdata prompts
|
||||||
RUN apt-get update && apt-get install -y \
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
ca-certificates \
|
|
||||||
fonts-liberation \
|
|
||||||
libasound2 \
|
|
||||||
libatk-bridge2.0-0 \
|
|
||||||
libatk1.0-0 \
|
|
||||||
libcups2 \
|
|
||||||
libdbus-1-3 \
|
|
||||||
libdrm2 \
|
|
||||||
libgbm1 \
|
|
||||||
libgtk-3-0 \
|
|
||||||
libnspr4 \
|
|
||||||
libnss3 \
|
|
||||||
libx11-xcb1 \
|
|
||||||
libxcomposite1 \
|
|
||||||
libxdamage1 \
|
|
||||||
libxrandr2 \
|
|
||||||
xdg-utils \
|
|
||||||
wget \
|
|
||||||
--no-install-recommends && \
|
|
||||||
rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
# Set working directory
|
# Install Node.js, Chromium and minimal runtime libs
|
||||||
WORKDIR /usr/src/app
|
# Note: chromium package on Debian provides /usr/bin/chromium
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
ca-certificates curl gnupg \
|
||||||
|
nodejs npm \
|
||||||
|
chromium \
|
||||||
|
# Minimal GUI/Chromium runtime libs often needed by Playwright Chromium
|
||||||
|
libx11-6 libxcomposite1 libxdamage1 libxrandr2 libxkbcommon0 \
|
||||||
|
libgtk-3-0 libnss3 libdrm2 libgbm1 libasound2 fonts-liberation \
|
||||||
|
# Useful for font rendering
|
||||||
|
fonts-dejavu-core \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Copy package files and install dependencies
|
# App directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install only production deps
|
||||||
COPY package*.json ./
|
COPY package*.json ./
|
||||||
COPY ignore-domains.txt ./
|
ENV CI=true
|
||||||
RUN npm ci
|
RUN npm ci --omit=dev
|
||||||
|
|
||||||
# Install Playwright browsers (Chromium)
|
# Copy source
|
||||||
RUN npx playwright install chromium
|
|
||||||
|
|
||||||
# Copy app sources
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
# Expose port
|
# Security: run as non-root
|
||||||
|
RUN useradd -ms /bin/bash nodeuser && chown -R nodeuser:nodeuser /app
|
||||||
|
USER nodeuser
|
||||||
|
|
||||||
|
# Environment for service
|
||||||
|
ENV PORT=3000 \
|
||||||
|
# Ensure Playwright uses system Chromium and does not download browsers
|
||||||
|
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 \
|
||||||
|
PLAYWRIGHT_BROWSERS_PATH=0 \
|
||||||
|
# Explicit executable if needed in code; here server uses default, so optional
|
||||||
|
CHROMIUM_PATH=/usr/bin/chromium
|
||||||
|
|
||||||
|
# Expose service port
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
|
||||||
# Run the service
|
# Start the service
|
||||||
CMD ["npm", "start"]
|
CMD ["node", "server.js"]
|
||||||
|
|
||||||
|
|
|
||||||
37
server.js
37
server.js
|
|
@ -1,9 +1,22 @@
|
||||||
|
// server.js
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const { chromium } = require('playwright');
|
const { chromium } = require('playwright');
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
const port = process.env.PORT || 3000;
|
const port = process.env.PORT || 3000;
|
||||||
|
|
||||||
|
// Использовать системный Chromium, если задан путь (например, /usr/bin/chromium в Debian)
|
||||||
|
const executablePath = process.env.CHROMIUM_PATH || undefined; // можно оставить undefined, если Chromium в PATH [1][2]
|
||||||
|
|
||||||
|
// Базовый набор флагов для контейнера без systemd/dbus и без install-deps
|
||||||
|
const chromiumArgs = [
|
||||||
|
'--no-sandbox', // запуск без setuid sandbox в контейнере [14]
|
||||||
|
'--disable-setuid-sandbox', // отключение setuid sandbox [14]
|
||||||
|
'--disable-dev-shm-usage', // использовать /tmp вместо /dev/shm (если нет --ipc=host) [15][16]
|
||||||
|
'--disable-gpu', // headless окружение [14]
|
||||||
|
'--no-zygote', // упрощение процессов в контейнере [14]
|
||||||
|
];
|
||||||
|
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
|
|
||||||
function extractDomain(url) {
|
function extractDomain(url) {
|
||||||
|
|
@ -23,12 +36,17 @@ app.get('/domains', async (req, res) => {
|
||||||
|
|
||||||
const url = `https://${domain}`;
|
const url = `https://${domain}`;
|
||||||
const seenDomains = new Set();
|
const seenDomains = new Set();
|
||||||
|
let browser;
|
||||||
|
let context;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const browser = await chromium.launch({
|
browser = await chromium.launch({
|
||||||
args: ['--no-sandbox', '--disable-setuid-sandbox']
|
executablePath, // берётся из CHROMIUM_PATH при наличии [1][2]
|
||||||
|
headless: true, // явный headless режим для контейнера [14]
|
||||||
|
args: chromiumArgs, // флаги для стабильности в Docker [15][14]
|
||||||
});
|
});
|
||||||
const context = await browser.newContext();
|
|
||||||
|
context = await browser.newContext();
|
||||||
const page = await context.newPage();
|
const page = await context.newPage();
|
||||||
|
|
||||||
page.on('request', request => {
|
page.on('request', request => {
|
||||||
|
|
@ -37,15 +55,20 @@ app.get('/domains', async (req, res) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
await page.goto(url, { waitUntil: 'load', timeout: 30000 });
|
await page.goto(url, { waitUntil: 'load', timeout: 30000 });
|
||||||
|
|
||||||
|
// Фильтрация доменов после закрытия страницы
|
||||||
|
await context.close();
|
||||||
await browser.close();
|
await browser.close();
|
||||||
|
|
||||||
// Фильтрация доменов
|
const filteredDomains = Array.from(seenDomains)
|
||||||
const filteredDomains = Array.from(seenDomains).filter(d =>
|
.filter(d => !d.includes('doubleclick') && !d.includes('google'))
|
||||||
!d.includes('doubleclick') && !d.includes('google')
|
.sort();
|
||||||
).sort();
|
|
||||||
|
|
||||||
res.json({ domains: filteredDomains });
|
res.json({ domains: filteredDomains });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
// Безопасно закрыть ресурсы при ошибке
|
||||||
|
try { if (context) await context.close(); } catch {}
|
||||||
|
try { if (browser) await browser.close(); } catch {}
|
||||||
res.status(500).json({ error: e.message || 'Internal server error' });
|
res.status(500).json({ error: e.message || 'Internal server error' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue