File size: 4,910 Bytes
77f6a35 abc92ad 384ae5b 77f6a35 abc92ad 77f6a35 abc92ad 77f6a35 abc92ad 384ae5b abc92ad 384ae5b 77f6a35 abc92ad 384ae5b abc92ad 384ae5b 77f6a35 abc92ad 384ae5b 77f6a35 abc92ad 77f6a35 abc92ad 384ae5b abc92ad 77f6a35 abc92ad 77f6a35 384ae5b 77f6a35 abc92ad 77f6a35 384ae5b abc92ad 77f6a35 384ae5b abc92ad 77f6a35 abc92ad 384ae5b abc92ad 384ae5b abc92ad 384ae5b abc92ad 77f6a35 384ae5b 77f6a35 abc92ad 384ae5b 77f6a35 abc92ad 384ae5b abc92ad 384ae5b abc92ad 384ae5b 77f6a35 abc92ad 77f6a35 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
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);
}); |