brat / app.js
siputzx's picture
Update app.js
abc92ad verified
raw
history blame
4.91 kB
import express from 'express';
import { chromium } from 'playwright';
import cors from 'cors';
import dotenv from 'dotenv';
import os from 'os';
import sharp from 'sharp';
import cluster from 'cluster';
import { cpus } from 'os';
dotenv.config();
const config = {
maxTextLength: 100,
viewport: { width: 1920, height: 1080 },
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
concurrentBrowsers: cpus().length // Gunakan jumlah core CPU
};
class BratGeneratorService {
constructor() {
this.browsers = [];
this.pages = [];
this.isInitialized = false;
}
async initialize() {
if (this.isInitialized) return;
const launchPromises = Array.from({ length: config.concurrentBrowsers }, async () => {
const browser = await chromium.launch({
headless: true,
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-dev-shm-usage'
]
});
const context = await browser.newContext({
viewport: config.viewport,
userAgent: config.userAgent
});
await context.route('**/*', (route) => {
const url = route.request().url();
if (url.endsWith('.png') || url.endsWith('.jpg') || url.includes('google-analytics')) {
return route.abort();
}
route.continue();
});
const page = await context.newPage();
await page.goto('https://www.bratgenerator.com/', {
waitUntil: 'domcontentloaded',
timeout: 5000
});
try {
await page.click('#onetrust-accept-btn-handler', { timeout: 2000 });
} catch { }
await page.evaluate(() => setupTheme('white'));
this.browsers.push(browser);
this.pages.push(page);
});
await Promise.all(launchPromises);
this.isInitialized = true;
}
async generateBrat(text, pageIndex = 0) {
if (!this.pages[pageIndex]) {
pageIndex = 0; // Reset jika index tidak valid
}
const page = this.pages[pageIndex];
await page.fill('#textInput', text);
const overlay = page.locator('#textOverlay');
const pngBuffer = await overlay.screenshot({
timeout: 3000,
type: 'png'
});
return sharp(pngBuffer)
.webp({ quality: 80 })
.toBuffer();
}
async close() {
const closePromises = this.browsers.map(browser => browser.close());
await Promise.all(closePromises);
this.browsers = [];
this.pages = [];
this.isInitialized = false;
}
}
const bratService = new BratGeneratorService();
async function startServer() {
const app = express();
app.use(express.json());
app.use(cors());
// Middleware untuk membatasi panjang teks
const validateTextLength = (req, res, next) => {
const { q } = req.query;
if (!q || q.length > config.maxTextLength) {
return res.status(400).json({
status: false,
message: `Teks harus diisi dan maksimal ${config.maxTextLength} karakter`
});
}
next();
};
app.get('*', validateTextLength, async (req, res) => {
try {
const { q } = req.query;
const pageIndex = Math.floor(Math.random() * config.concurrentBrowsers);
const imageBuffer = await bratService.generateBrat(q, pageIndex);
res.set('Content-Type', 'image/webp');
res.send(imageBuffer);
} catch (error) {
console.error(error);
res.status(500).json({
status: false,
message: 'Error generating image',
error: process.env.NODE_ENV === 'development' ? error.message : undefined
});
}
});
const PORT = process.env.PORT || 7860;
await bratService.initialize();
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
console.log(`Worker PID: ${process.pid}`);
});
}
// Gunakan cluster untuk scaling
if (cluster.isPrimary) {
console.log(`Primary ${process.pid} is running`);
// Fork workers.
for (let i = 0; i < cpus().length; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code, signal) => {
console.log(`worker ${worker.process.pid} died`);
cluster.fork(); // Restart worker
});
} else {
startServer().catch(console.error);
}
process.on('SIGINT', async () => {
await bratService.close();
process.exit(0);
});