Skip to content

Commit 1d8e12f

Browse files
first commit
0 parents  commit 1d8e12f

File tree

5 files changed

+1593
-0
lines changed

5 files changed

+1593
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/node_modules

Dockerfile

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
FROM node:22-slim AS base
2+
ENV PNPM_HOME="/pnpm"
3+
ENV PATH="$PNPM_HOME:$PATH"
4+
RUN corepack enable
5+
WORKDIR /app
6+
COPY package.json pnpm-lock.yaml ./
7+
RUN corepack prepare
8+
9+
FROM base AS prod-deps
10+
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod --frozen-lockfile
11+
12+
FROM base AS build
13+
COPY . .
14+
15+
FROM base
16+
COPY --from=prod-deps /app/node_modules /app/node_modules
17+
COPY --from=build /app/index.js /app/index.js
18+
RUN pnpm playwright install --with-deps chromium
19+
CMD ["pnpm", "start"]

index.js

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import { createError } from "h3";
2+
import {
3+
createApp,
4+
createRouter,
5+
defineEventHandler,
6+
toNodeListener,
7+
getValidatedQuery,
8+
} from "h3";
9+
import { createServer } from "node:http";
10+
import { chromium } from "playwright";
11+
import { playAudit } from "playwright-lighthouse";
12+
import { z } from "zod";
13+
14+
const port = process.env.PORT || 3000;
15+
const signature = process.env.APPWRITE_BROWSER_SECRET;
16+
if (!signature) {
17+
throw new Error("SIGNATURE environment variable is required");
18+
}
19+
20+
const app = createApp({
21+
onRequest: (event) => {
22+
const auth = event.headers.get("Authorization");
23+
const [type, token] = auth.split(" ");
24+
if (token === null)
25+
throw createError({
26+
status: 400,
27+
statusMessage: "Unauthorized",
28+
message: "Missing signature",
29+
});
30+
31+
if (type !== "Bearer" || token !== signature)
32+
throw createError({
33+
status: 401,
34+
statusMessage: "Unauthorized",
35+
message: "Invalid signature",
36+
});
37+
},
38+
});
39+
const router = createRouter();
40+
41+
console.log("Chromium starting...");
42+
const browser = await chromium.launch({
43+
args: ["--remote-debugging-port=9222"],
44+
});
45+
console.log("Chromium started!");
46+
47+
app.use(router);
48+
49+
/** @type {import('playwright').BrowserContext} defaultContext */
50+
const defaultContext = {
51+
viewport: {
52+
width: 1280,
53+
height: 720,
54+
},
55+
};
56+
57+
const screenshotParams = z.object({
58+
url: z.string().url(),
59+
});
60+
router.get(
61+
"/screenshot",
62+
defineEventHandler(async (event) => {
63+
const query = await getValidatedQuery(event, screenshotParams.parse);
64+
const context = await browser.newContext(defaultContext);
65+
const page = await context.newPage();
66+
await page.goto(query.url);
67+
const screen = await page.screenshot();
68+
await context.close();
69+
return screen;
70+
}),
71+
);
72+
73+
const lighthouseParams = z.object({
74+
url: z.string().url(),
75+
formats: z.array(z.enum(["html", "json"])).default(["json"]),
76+
});
77+
router.get(
78+
"/lighthouse",
79+
defineEventHandler(async (event) => {
80+
const query = await getValidatedQuery(event, lighthouseParams.parse);
81+
const context = await browser.newContext(defaultContext);
82+
const page = await context.newPage();
83+
await page.goto(query.url);
84+
const results = await playAudit({
85+
reports: {
86+
formats: {
87+
html: true,
88+
},
89+
},
90+
page: page,
91+
port: 9222,
92+
thresholds: {
93+
"best-practices": 0,
94+
accessibility: 0,
95+
performance: 0,
96+
pwa: 0,
97+
seo: 0,
98+
},
99+
});
100+
await context.close();
101+
return results;
102+
}),
103+
);
104+
105+
router.use(
106+
"/health",
107+
defineEventHandler(async (event) => {
108+
return {
109+
status: browser.isConnected() ? "ok" : "error",
110+
};
111+
}),
112+
);
113+
114+
createServer(toNodeListener(app)).listen(port);
115+
116+
console.log(`Server running on port http://0.0.0.0:${port}`);

package.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"name": "browser",
3+
"version": "1.0.0",
4+
"description": "",
5+
"main": "index.js",
6+
"type": "module",
7+
"scripts": {
8+
"start": "node index.js",
9+
"test": "echo \"Error: no test specified\" && exit 1"
10+
},
11+
"keywords": [],
12+
"author": "",
13+
"license": "ISC",
14+
"packageManager": "pnpm@9.12.1+sha512.e5a7e52a4183a02d5931057f7a0dbff9d5e9ce3161e33fa68ae392125b79282a8a8a470a51dfc8a0ed86221442eb2fb57019b0990ed24fab519bf0e1bc5ccfc4",
15+
"dependencies": {
16+
"@hono/node-server": "^1.13.2",
17+
"h3": "^1.13.0",
18+
"hono": "^4.6.6",
19+
"playwright": "^1.48.1",
20+
"playwright-lighthouse": "^4.0.0",
21+
"zod": "^3.23.8"
22+
}
23+
}

0 commit comments

Comments
 (0)