feat: initial codebase v0.4.1
Some checks failed
Deploy to Staging / build-and-deploy (push) Failing after 9m44s

- Extract complete codebase from running staging pod
- Add Dockerfile with multi-stage build for Node.js + Puppeteer
- Configure CI/CD workflows for staging and production deployment
- Include all source files, configs, and public assets
This commit is contained in:
OpenClaw DevOps 2026-02-19 17:05:16 +00:00
commit b58f634318
28 changed files with 5669 additions and 0 deletions

68
src/routes/playground.ts Normal file
View file

@ -0,0 +1,68 @@
import { Router } from "express";
import { takeScreenshot } from "../services/screenshot.js";
import { addWatermark } from "../services/watermark.js";
import logger from "../services/logger.js";
import rateLimit from "express-rate-limit";
export const playgroundRouter = Router();
// 5 requests per hour per IP
const playgroundLimiter = rateLimit({
windowMs: 60 * 60 * 1000,
max: 5,
standardHeaders: true,
legacyHeaders: false,
message: { error: "Playground rate limit exceeded (5 requests/hour). Get an API key for unlimited access.", upgrade: "https://snapapi.eu/#pricing" },
keyGenerator: (req) => req.ip || req.socket.remoteAddress || "unknown",
});
playgroundRouter.post("/", playgroundLimiter, async (req, res) => {
const { url, format, width, height } = req.body;
if (!url || typeof url !== "string") {
res.status(400).json({ error: "Missing required parameter: url" });
return;
}
// Enforce reasonable limits for playground
const safeWidth = Math.min(Math.max(parseInt(width, 10) || 1280, 320), 1920);
const safeHeight = Math.min(Math.max(parseInt(height, 10) || 800, 200), 1080);
const safeFormat = ["png", "jpeg", "webp"].includes(format) ? format : "png";
try {
const result = await takeScreenshot({
url,
format: safeFormat as "png" | "jpeg" | "webp",
width: safeWidth,
height: safeHeight,
fullPage: false,
quality: safeFormat === "png" ? undefined : 70,
deviceScale: 1,
});
// Add watermark
const watermarked = await addWatermark(result.buffer, safeWidth, safeHeight);
res.setHeader("Content-Type", result.contentType);
res.setHeader("Content-Length", watermarked.length);
res.setHeader("Cache-Control", "no-store");
res.setHeader("X-Playground", "true");
res.send(watermarked);
} catch (err: any) {
logger.error({ err: err.message, url }, "Playground screenshot failed");
if (err.message === "QUEUE_FULL") {
res.status(503).json({ error: "Service busy. Try again shortly." });
return;
}
if (err.message === "SCREENSHOT_TIMEOUT") {
res.status(504).json({ error: "Screenshot timed out." });
return;
}
if (err.message.includes("blocked") || err.message.includes("not allowed") || err.message.includes("Invalid URL")) {
res.status(400).json({ error: err.message });
return;
}
res.status(500).json({ error: "Screenshot failed" });
}
});