Skip to content

Commit 27dbca7

Browse files
authored
chore: add puppeteer & chrome dependencies (#102)
1 parent f2d78e1 commit 27dbca7

File tree

10 files changed

+870
-8
lines changed

10 files changed

+870
-8
lines changed

.dockerignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,4 @@ node_modules
3434
/priv/domain_blacklist.txt
3535
/priv/plts
3636
/priv/db
37+
/priv/puppeteer

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ algora-*.tar
2727

2828
# Ignore assets that are produced by build tools.
2929
/priv/static/assets/
30+
/priv/puppeteer
3031

3132
# Ignore digested assets cache.
3233
/priv/static/cache_manifest.json

Dockerfile

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,17 @@
1414
ARG ELIXIR_VERSION=1.18.1
1515
ARG OTP_VERSION=27.2
1616
ARG DEBIAN_VERSION=bookworm-20241223-slim
17+
ARG NODE_VERSION=23-bookworm-slim
1718

1819
ARG BUILDER_IMAGE="hexpm/elixir:${ELIXIR_VERSION}-erlang-${OTP_VERSION}-debian-${DEBIAN_VERSION}"
1920
ARG RUNNER_IMAGE="debian:${DEBIAN_VERSION}"
21+
ARG NODE_IMAGE="node:${NODE_VERSION}"
2022

23+
FROM ${NODE_IMAGE} as node_stage
24+
25+
ENV PUPPETEER_CACHE_DIR="/app/puppeteer"
26+
27+
RUN npx --yes puppeteer browsers install
2128

2229
FROM ${BUILDER_IMAGE} as builder
2330

@@ -52,7 +59,7 @@ COPY lib lib
5259

5360
COPY assets assets
5461

55-
COPY --from=node:23-bookworm-slim /usr/local/bin /usr/local/bin
62+
COPY --from=node_stage /usr/local/bin /usr/local/bin
5663

5764
# compile assets
5865
RUN mix assets.deploy
@@ -77,9 +84,9 @@ RUN apt-get update -y && \
7784
# TODO: remove after migration
7885
RUN apt-get update -y && apt-get install -y postgresql-client
7986

80-
COPY --from=node:23-bookworm-slim /usr/local/bin /usr/local/bin
87+
COPY --from=node_stage /usr/local/bin /usr/local/bin
8188

82-
RUN npm install -g @algora/[email protected]
89+
RUN apt-get update -y && apt-get install -y ca-certificates fonts-liberation libasound2 libatk-bridge2.0-0 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgbm1 libgcc1 libglib2.0-0 libgtk-3-0 libnspr4 libnss3 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 lsb-release wget xdg-utils
8390

8491
# Set the locale
8592
RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && locale-gen
@@ -94,9 +101,12 @@ RUN chown nobody /app
94101
# set runner ENV
95102
ENV MIX_ENV="prod"
96103

97-
# Only copy the final release from the build stage
104+
# Copy the final release from the build stage
98105
COPY --from=builder --chown=nobody:root /app/_build/${MIX_ENV}/rel/algora ./
99106

107+
# Copy the puppeteer cache from the node stage
108+
COPY --from=node_stage --chown=nobody:root /app/puppeteer ./puppeteer
109+
100110
USER nobody
101111

102112
# If using an environment that doesn't automatically reap zombie processes, it is

assets/build.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,20 @@ let optsServer = {
4646
],
4747
};
4848

49+
let optsPuppeteer = {
50+
entryPoints: ["js/puppeteer-img.js"],
51+
platform: "node",
52+
bundle: true,
53+
minify: true,
54+
target: "node19.6.1",
55+
conditions: [],
56+
outdir: "../priv/puppeteer",
57+
logLevel: "info",
58+
sourcemap: watch ? "inline" : false,
59+
tsconfig: "./tsconfig.json",
60+
plugins: [],
61+
};
62+
4963
if (watch) {
5064
esbuild
5165
.context(optsClient)
@@ -56,7 +70,13 @@ if (watch) {
5670
.context(optsServer)
5771
.then((ctx) => ctx.watch())
5872
.catch((_error) => process.exit(1));
73+
74+
esbuild
75+
.context(optsPuppeteer)
76+
.then((ctx) => ctx.watch())
77+
.catch((_error) => process.exit(1));
5978
} else {
6079
esbuild.build(optsClient);
6180
esbuild.build(optsServer);
81+
esbuild.build(optsPuppeteer);
6282
}

assets/js/app.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -617,7 +617,11 @@ topbar.config({
617617
barColors: { 0: "rgba(5, 150, 105, 1)" },
618618
shadowColor: "rgba(0, 0, 0, .3)",
619619
});
620-
window.addEventListener("phx:page-loading-start", (info) => topbar.show(300));
620+
window.addEventListener("phx:page-loading-start", (info) => {
621+
if (!window.location.search.includes("screenshot")) {
622+
topbar.show(300);
623+
}
624+
});
621625
window.addEventListener("phx:page-loading-stop", (info) => topbar.hide());
622626

623627
// Accessible routing

assets/js/puppeteer-img.js

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import puppeteer from "puppeteer";
2+
3+
function parseArgs() {
4+
const args = process.argv.slice(2);
5+
const options = {
6+
type: "png",
7+
path: null,
8+
width: "800",
9+
height: "600",
10+
scaleFactor: "1",
11+
x: null,
12+
y: null,
13+
clipWidth: null,
14+
clipHeight: null,
15+
};
16+
17+
for (let i = 0; i < args.length; i++) {
18+
let arg = args[i];
19+
let value = null;
20+
21+
[arg, value] = arg.split("=");
22+
23+
switch (arg) {
24+
case "-t":
25+
case "--type":
26+
options.type = value;
27+
break;
28+
case "-p":
29+
case "--path":
30+
options.path = value;
31+
break;
32+
case "-w":
33+
case "--width":
34+
options.width = value;
35+
break;
36+
case "-h":
37+
case "--height":
38+
options.height = value;
39+
break;
40+
case "-s":
41+
case "--scale-factor":
42+
options.scaleFactor = value;
43+
break;
44+
case "-x":
45+
case "--x":
46+
options.x = value;
47+
break;
48+
case "-y":
49+
case "--y":
50+
options.y = value;
51+
break;
52+
case "--clip-width":
53+
options.clipWidth = value;
54+
break;
55+
case "--clip-height":
56+
options.clipHeight = value;
57+
break;
58+
}
59+
}
60+
61+
// URL is the first non-option argument
62+
options.url = args.find((arg) => !arg.startsWith("-"));
63+
return options;
64+
}
65+
66+
function _validateInteger(value) {
67+
const parsed = parseInt(value);
68+
if (value && !parsed) {
69+
console.error("Number values must be valid integer");
70+
return null;
71+
}
72+
return parsed;
73+
}
74+
75+
(async () => {
76+
const options = parseArgs();
77+
let screenshotOptions = {};
78+
let viewportOptions = {};
79+
80+
if (!options.url) {
81+
console.error("URL required");
82+
return;
83+
}
84+
85+
viewportOptions.width = _validateInteger(options.width) || 800;
86+
viewportOptions.height = _validateInteger(options.height) || 600;
87+
viewportOptions.deviceScaleFactor =
88+
_validateInteger(options.scaleFactor) || 1;
89+
screenshotOptions.type = ["jpeg", "png"].includes(options.type)
90+
? options.type
91+
: "png";
92+
screenshotOptions.path = options.path || `./image.${screenshotOptions.type}`;
93+
94+
const clipParams = {
95+
x: options.x,
96+
y: options.y,
97+
width: options.clipWidth,
98+
height: options.clipHeight,
99+
};
100+
const hasClipParams = Object.values(clipParams).every((val) => val !== null);
101+
102+
if (hasClipParams) {
103+
screenshotOptions.clip = {};
104+
for (const [key, value] of Object.entries(clipParams)) {
105+
screenshotOptions.clip[key] = _validateInteger(value);
106+
}
107+
}
108+
109+
puppeteer
110+
.launch({
111+
devtools: false,
112+
args: ["--no-sandbox", "--disable-setuid-sandbox", "--single-process"],
113+
ignoreHTTPSErrors: true,
114+
})
115+
.then(async function (browser) {
116+
const page = await browser.newPage();
117+
await page.setViewport(viewportOptions);
118+
await page.goto(options.url, { waitUntil: ["networkidle2"] });
119+
await page.focus("body");
120+
await page.screenshot(screenshotOptions);
121+
122+
await page.close();
123+
await browser.close();
124+
process.stdout.write(screenshotOptions.path);
125+
});
126+
})();

assets/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"esbuild": "^0.24.0",
1717
"esbuild-plugin-import-glob": "^0.1.1",
1818
"esbuild-svelte": "^0.9.0",
19+
"puppeteer": "^24.4.0",
1920
"svelte": "^4.2.19",
2021
"svelte-preprocess": "^6.0.3",
2122
"tailwindcss-animate": "^1.0.7",

0 commit comments

Comments
 (0)