fix: docker, docs, workflows

This commit is contained in:
Artemy 2024-05-16 16:44:49 +03:00
parent 50a8c0aed8
commit b3f6fb715d
13 changed files with 503 additions and 366 deletions

2
.dockerignore Normal file
View file

@ -0,0 +1,2 @@
node_modules
dist

View file

@ -1,5 +1,9 @@
HOST="0.0.0.0"
PORT=8080
TIMEOUT=0
TIMEOUT=0 # Max timout for server response, 0 means no timeout
REVERSE_PROXY=false # Set true if app running behind reverse_proxy
# Maximum timeout for waiting for page load, once the page is
# loaded or the timeout is exceeded, rendering is terminated
# and the page is rendered
WAIT_FOR_PAGE_LOAD_TIMEOUT=2000
REVERSE_PROXY=false

View file

@ -5,15 +5,19 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v2
uses: actions/setup-node@v4
with:
node-version: '18'
node-version: '20'
- uses: pnpm/action-setup@v4
with:
version: 9
- name: Install dependencies
run: npm install
run: pnpm install
- name: Start build
run: npm run build
run: pnpm run build

View file

@ -3,6 +3,10 @@ name: Create and publish a Docker image
# Configures this workflow to run every time a change is pushed to the branch called `release`.
on:
push:
branches:
- main
- dev
release:
types:
- published
@ -20,13 +24,13 @@ jobs:
permissions:
contents: read
packages: write
#
steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v4
# Uses the `docker/login-action` action to log in to the Container registry registry using the account and password that will publish the packages. Once published, the packages are scoped to the account defined here.
- name: Log in to the Container registry
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
@ -34,14 +38,14 @@ jobs:
# This step uses [docker/metadata-action](https://github.com/docker/metadata-action#about) to extract tags and labels that will be applied to the specified image. The `id` "meta" allows the output of this step to be referenced in a subsequent step. The `images` value provides the base name for the tags and labels.
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
# This step uses the `docker/build-push-action` action to build the image, based on your repository's `Dockerfile`. If the build succeeds, it pushes the image to GitHub Packages.
# It uses the `context` parameter to define the build's context as the set of files located in the specified path. For more information, see "[Usage](https://github.com/docker/build-push-action#usage)" in the README of the `docker/build-push-action` repository.
# It uses the `tags` and `labels` parameters to tag and label the image with the output from the "meta" step.
- name: Build and push Docker image
uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4
uses: docker/build-push-action@v5
with:
context: .
push: true

View file

@ -5,15 +5,19 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v2
uses: actions/setup-node@v4
with:
node-version: '18'
node-version: '20'
- uses: pnpm/action-setup@v4
with:
version: 9
- name: Install dependencies
run: npm install
run: pnpm install
- name: Check formatting
run: npm run format:check
- name: Start build
run: pnpm run format:check

View file

@ -1,12 +1,27 @@
FROM node:18-alpine as build
FROM node:20-alpine as base
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
COPY . /app
WORKDIR /app
COPY . .
RUN npm install
RUN npm run build
FROM node:18-alpine as run
FROM base AS prod-deps
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod --frozen-lockfile
FROM base AS build
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
RUN pnpm run build
FROM node:20-alpine as run
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
WORKDIR /app
COPY --from=build /app/dist/ /app/package*.json ./
RUN npm install --omit=dev
CMD npm run start:docker
COPY --from=prod-deps /app/node_modules /app/node_modules
COPY --from=build /app/dist /app/dist/
COPY --from=build /app/package.json /app/package.json
EXPOSE 8080
CMD [ "pnpm", "start" ]

View file

@ -2,8 +2,9 @@
Proxy that renders client-side JavaScript apps (e.g. React apps) on server and returns the resulting HTML code.
- JS is disabled/unsupported, no proxy: blank page or "Enable JS to continue"
- With webder: all desired content, no need to use JS interpreter
| Response without proxy | With webder |
| ----------------------------------------------------------------- | -------------------------------------------------- |
| JS is disabled/unsupported, blank page or "Enable JS to continue" | all desired content, no need to use JS interpreter |
> [!WARNING]
> No HTML purification is performed, so passing code directly from webder can lead to XSS attacks. Local network requests are not blocked too, check domains/IPs to avoid SSRF attacks.
@ -11,13 +12,13 @@ Proxy that renders client-side JavaScript apps (e.g. React apps) on server and r
## Usage
`/render?url=...`
The proxy is accessible at `/render?url=...` by default.
For available config fields, check `.env.example`.
Docker is supported.
```bash
npm install
npm run build
npm start
pnpm install
pnpm run build
pnpm start
```

View file

@ -1,10 +1,10 @@
version: "3"
version: '3'
services:
txtdot:
image: ghcr.io/txtdot/webder:latest
ports:
- "8080:8080"
- '8080:8080'
restart: unless-stopped
volumes:
- ".env:/app/.env"
- '.env:/app/.env'

View file

@ -1,6 +1,6 @@
{
"name": "webder",
"version": "1.0.0",
"version": "1.0.1",
"description": "Http/s proxy that render pages with js and returns HTML",
"main": "dist/app.js",
"private": true,
@ -23,19 +23,20 @@
"license": "MIT",
"devDependencies": {
"@types/dompurify": "^3.0.5",
"@types/node": "^20.11.30",
"@typescript-eslint/eslint-plugin": "^7.3.1",
"@typescript-eslint/parser": "^7.3.1",
"@types/node": "^20.12.12",
"@typescript-eslint/eslint-plugin": "^7.9.0",
"@typescript-eslint/parser": "^7.9.0",
"copyfiles": "^2.4.1",
"eslint": "^8.57.0",
"tsc-watch": "^6.0.4",
"typescript": "^5.4.3"
"tsc-watch": "^6.2.0",
"typescript": "^5.4.5"
},
"dependencies": {
"@fastify/one-line-logger": "^1.3.0",
"dotenv": "^16.4.5",
"fastify": "^4.26.2",
"fastify": "^4.27.0",
"prettier": "^3.2.5",
"puppeteer": "^22.6.0",
"puppeteer": "^22.9.0",
"puppeteer-extra": "^3.3.6",
"puppeteer-extra-plugin-adblocker": "^2.13.6",
"puppeteer-extra-plugin-stealth": "^2.11.2"

710
pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
import { ConfigService } from "./config/config.service";
import Fastify from "fastify";
import getConfig from "./config/main";
import { puppeteer } from "./puppeteer";
import { ConfigService } from './config/config.service';
import Fastify from 'fastify';
import getConfig from './config/main';
import { puppeteer } from './puppeteer';
class App {
config: ConfigService;
@ -10,21 +10,25 @@ class App {
}
listen() {
const fastify = Fastify({
logger: true,
logger: {
transport: {
target: '@fastify/one-line-logger',
},
},
trustProxy: this.config.reverse_proxy,
connectionTimeout: this.config.timeout,
});
fastify.get<{ Querystring: { url: string } }>(
"/render",
'/render',
{
schema: {
querystring: {
type: "object",
type: 'object',
properties: {
url: { type: "string" },
url: { type: 'string' },
},
required: ["url"],
required: ['url'],
},
},
},
@ -40,14 +44,14 @@ class App {
idleTime: 100,
});
} catch {
reply.header("x-message", "Page load timeout");
reply.header('x-message', 'Page load timeout');
}
const data = await page.content();
browser.close();
reply.type("text/html");
reply.type('text/html');
return data;
}
);

View file

@ -1,4 +1,4 @@
import { config } from "dotenv";
import { config } from 'dotenv';
export class ConfigService {
public readonly host: string;
@ -9,7 +9,7 @@ export class ConfigService {
constructor() {
config();
this.host = process.env.HOST || "0.0.0.0";
this.host = process.env.HOST || '0.0.0.0';
this.port = Number(process.env.PORT) || 8080;
this.timeout = Number(process.env.TIMEOUT) || 0;
@ -21,6 +21,6 @@ export class ConfigService {
parseBool(value: string | undefined, def: boolean): boolean {
if (!value) return def;
return value === "true" || value === "1";
return value === 'true' || value === '1';
}
}

View file

@ -1,4 +1,4 @@
import { ConfigService } from "./config.service";
import { ConfigService } from './config.service';
let configSvc: ConfigService | undefined;