siputzx commited on
Commit
abc92ad
·
verified ·
1 Parent(s): 71d7cba

Update app.js

Browse files
Files changed (1) hide show
  1. app.js +114 -35
app.js CHANGED
@@ -1,86 +1,165 @@
1
  import express from 'express';
2
- import { chromium } from 'playwright-extra';
3
- import stealth from 'playwright-extra/plugins/stealth';
 
 
 
4
  import cluster from 'cluster';
5
  import { cpus } from 'os';
6
- import sharp from 'sharp';
7
 
8
- chromium.use(stealth());
9
 
10
- const BROWSERS_COUNT = cpus().length;
11
- const PORT = process.env.PORT || 7860;
 
 
 
 
12
 
13
- class FastBratGenerator {
14
  constructor() {
15
  this.browsers = [];
16
  this.pages = [];
 
17
  }
18
 
19
- async init() {
20
- const promises = Array(BROWSERS_COUNT).fill().map(async () => {
21
- const browser = await chromium.launch({ headless: true });
22
- const context = await browser.newContext({ viewport: { width: 1920, height: 1080 } });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  const page = await context.newPage();
24
-
25
- await page.goto('https://www.bratgenerator.com/', { waitUntil: 'domcontentloaded' });
26
-
 
 
 
 
 
 
 
 
27
  this.browsers.push(browser);
28
  this.pages.push(page);
29
  });
30
 
31
- await Promise.all(promises);
 
32
  }
33
 
34
- async generate(text) {
35
- const page = this.pages[Math.floor(Math.random() * this.pages.length)];
 
 
 
 
36
  await page.fill('#textInput', text);
 
37
 
38
- const screenshot = await page.locator('#textOverlay').screenshot({
39
- type: 'png',
40
- timeout: 2000
41
  });
42
 
43
- return sharp(screenshot)
44
- .webp({ quality: 85, speed: 6 })
45
  .toBuffer();
46
  }
47
 
48
  async close() {
49
- await Promise.all(this.browsers.map(b => b.close()));
 
 
 
 
50
  }
51
  }
52
 
53
- const bratGenerator = new FastBratGenerator();
54
 
55
  async function startServer() {
56
  const app = express();
57
  app.use(express.json());
 
 
 
 
 
 
 
 
 
 
 
 
 
58
 
59
- app.get('/', async (req, res) => {
60
  try {
61
  const { q } = req.query;
62
- if (!q) return res.status(400).send('Text required');
63
 
64
- const imageBuffer = await bratGenerator.generate(q);
65
- res.contentType('image/webp');
66
  res.send(imageBuffer);
67
  } catch (error) {
68
- res.status(500).send('Generation failed');
 
 
 
 
 
69
  }
70
  });
71
 
72
- await bratGenerator.init();
73
- app.listen(PORT, () => console.log(`Running on ${PORT}`));
 
 
 
 
 
74
  }
75
 
 
76
  if (cluster.isPrimary) {
77
- cpus().forEach(() => cluster.fork());
78
- cluster.on('exit', (worker) => cluster.fork());
 
 
 
 
 
 
 
 
 
79
  } else {
80
- startServer();
81
  }
82
 
83
  process.on('SIGINT', async () => {
84
- await bratGenerator.close();
85
  process.exit(0);
86
  });
 
1
  import express from 'express';
2
+ import { chromium } from 'playwright';
3
+ import cors from 'cors';
4
+ import dotenv from 'dotenv';
5
+ import os from 'os';
6
+ import sharp from 'sharp';
7
  import cluster from 'cluster';
8
  import { cpus } from 'os';
 
9
 
10
+ dotenv.config();
11
 
12
+ const config = {
13
+ maxTextLength: 100,
14
+ viewport: { width: 1920, height: 1080 },
15
+ userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
16
+ concurrentBrowsers: cpus().length // Gunakan jumlah core CPU
17
+ };
18
 
19
+ class BratGeneratorService {
20
  constructor() {
21
  this.browsers = [];
22
  this.pages = [];
23
+ this.isInitialized = false;
24
  }
25
 
26
+ async initialize() {
27
+ if (this.isInitialized) return;
28
+
29
+ const launchPromises = Array.from({ length: config.concurrentBrowsers }, async () => {
30
+ const browser = await chromium.launch({
31
+ headless: true,
32
+ args: [
33
+ '--no-sandbox',
34
+ '--disable-setuid-sandbox',
35
+ '--disable-dev-shm-usage'
36
+ ]
37
+ });
38
+ const context = await browser.newContext({
39
+ viewport: config.viewport,
40
+ userAgent: config.userAgent
41
+ });
42
+
43
+ await context.route('**/*', (route) => {
44
+ const url = route.request().url();
45
+ if (url.endsWith('.png') || url.endsWith('.jpg') || url.includes('google-analytics')) {
46
+ return route.abort();
47
+ }
48
+ route.continue();
49
+ });
50
+
51
  const page = await context.newPage();
52
+ await page.goto('https://www.bratgenerator.com/', {
53
+ waitUntil: 'domcontentloaded',
54
+ timeout: 5000
55
+ });
56
+
57
+ try {
58
+ await page.click('#onetrust-accept-btn-handler', { timeout: 2000 });
59
+ } catch { }
60
+
61
+ await page.evaluate(() => setupTheme('white'));
62
+
63
  this.browsers.push(browser);
64
  this.pages.push(page);
65
  });
66
 
67
+ await Promise.all(launchPromises);
68
+ this.isInitialized = true;
69
  }
70
 
71
+ async generateBrat(text, pageIndex = 0) {
72
+ if (!this.pages[pageIndex]) {
73
+ pageIndex = 0; // Reset jika index tidak valid
74
+ }
75
+
76
+ const page = this.pages[pageIndex];
77
  await page.fill('#textInput', text);
78
+ const overlay = page.locator('#textOverlay');
79
 
80
+ const pngBuffer = await overlay.screenshot({
81
+ timeout: 3000,
82
+ type: 'png'
83
  });
84
 
85
+ return sharp(pngBuffer)
86
+ .webp({ quality: 80 })
87
  .toBuffer();
88
  }
89
 
90
  async close() {
91
+ const closePromises = this.browsers.map(browser => browser.close());
92
+ await Promise.all(closePromises);
93
+ this.browsers = [];
94
+ this.pages = [];
95
+ this.isInitialized = false;
96
  }
97
  }
98
 
99
+ const bratService = new BratGeneratorService();
100
 
101
  async function startServer() {
102
  const app = express();
103
  app.use(express.json());
104
+ app.use(cors());
105
+
106
+ // Middleware untuk membatasi panjang teks
107
+ const validateTextLength = (req, res, next) => {
108
+ const { q } = req.query;
109
+ if (!q || q.length > config.maxTextLength) {
110
+ return res.status(400).json({
111
+ status: false,
112
+ message: `Teks harus diisi dan maksimal ${config.maxTextLength} karakter`
113
+ });
114
+ }
115
+ next();
116
+ };
117
 
118
+ app.get('*', validateTextLength, async (req, res) => {
119
  try {
120
  const { q } = req.query;
121
+ const pageIndex = Math.floor(Math.random() * config.concurrentBrowsers);
122
 
123
+ const imageBuffer = await bratService.generateBrat(q, pageIndex);
124
+ res.set('Content-Type', 'image/webp');
125
  res.send(imageBuffer);
126
  } catch (error) {
127
+ console.error(error);
128
+ res.status(500).json({
129
+ status: false,
130
+ message: 'Error generating image',
131
+ error: process.env.NODE_ENV === 'development' ? error.message : undefined
132
+ });
133
  }
134
  });
135
 
136
+ const PORT = process.env.PORT || 7860;
137
+ await bratService.initialize();
138
+
139
+ app.listen(PORT, () => {
140
+ console.log(`Server running on port ${PORT}`);
141
+ console.log(`Worker PID: ${process.pid}`);
142
+ });
143
  }
144
 
145
+ // Gunakan cluster untuk scaling
146
  if (cluster.isPrimary) {
147
+ console.log(`Primary ${process.pid} is running`);
148
+
149
+ // Fork workers.
150
+ for (let i = 0; i < cpus().length; i++) {
151
+ cluster.fork();
152
+ }
153
+
154
+ cluster.on('exit', (worker, code, signal) => {
155
+ console.log(`worker ${worker.process.pid} died`);
156
+ cluster.fork(); // Restart worker
157
+ });
158
  } else {
159
+ startServer().catch(console.error);
160
  }
161
 
162
  process.on('SIGINT', async () => {
163
+ await bratService.close();
164
  process.exit(0);
165
  });