card / app.js
wudysoft's picture
Update app.js
13e582b verified
import express from 'express';
import { chromium } from 'playwright';
import cors from 'cors';
import axios from 'axios';
import dotenv from 'dotenv';
import os 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'
};
const app = express();
app.use(express.json());
app.use(cors());
app.get('/', (req, res) => {
res.send('<h1>Welcome to the Web Scraping API</h1>');
});
app.get('/discord', async (req, res) => {
const { logo, text, text1, text2, text3, text4, text5, text6, text7, text8, text9 } = req.query;
if (!(logo)) {
return res.status(400).json({ error: 'Missing logo parameters' });
}
const html = `<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>Discord Profile Card With Webhook</title>
<style>
@import url("https://fonts.googleapis.com/css?family=Roboto:400,400i,700");
* {
margin: 0;
padding: 0;
font-family: Roboto, sans-serif;
box-sizing: border-box;
}
body,
html {
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background: #0b0b0b;
}
.tooltip {
display: block;
position: absolute;
color: #b6b7b7;
background: #18191c;
padding: 0.4rem;
border-radius: 3px;
max-width: 150px;
width: max-content;
font-size: 0.8rem;
transform: scale(0);
transition: 0.055s ease-in-out transform;
z-index: 10;
box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.35);
-webkit-box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.35);
}
.tooltip-up {
bottom: 30px;
}
.tooltip-up::before {
content: "";
position: absolute;
bottom: -5px;
left: 50%;
transform: translateX(-50%);
width: 0;
height: 0;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-top: 6px solid #18191c;
}
.btn {
display: flex;
align-items: center;
justify-content: center;
text-decoration: none;
background-color: #535353;
padding: 10px;
border-radius: 3px;
color: #fff;
font-size: 0.85rem;
transition: 0.2s ease-in-out;
margin-top: 12px;
}
.btn:hover {
background-color: #747474;
}
/* Card */
.card-container {
display: flex;
flex-direction: row;
justify-content: space-between;
width: 1450px;
z-index: 0;
}
.card {
background: #292b2f;
width: 345px;
max-height: 95%;
height: max-content;
border-radius: 9px;
box-shadow: 0px 0px 16px 3px rgba(0, 0, 0, 0.2);
-webkit-box-shadow: 0px 0px 16px 3px rgba(0, 0, 0, 0.2);
scrollbar-width: none;
}
.card::-webkit-scrollbar {
display: none;
}
.card-header .banner {
width: 100%;
height: 60px;
background: #ef5b0d;
border-radius: 6px 6px 0 0;
overflow: hidden;
}
.card-header .banner-img {
width: 100%;
height: 120px;
background-position: center !important;
background-size: 100% auto !important;
border-radius: 6px 6px 0 0;
overflow: hidden;
}
.card-body {
padding: 15px;
position: relative;
}
.card-body .profile-header {
position: absolute;
display: flex;
flex-direction: row;
align-items: flex-end;
justify-content: space-between;
width: calc(100% - 30px);
top: -50px;
}
.card-body .profile-header .profil-logo {
position: relative;
border: 6px solid #292b2f;
border-radius: 50%;
}
.card-body .profile-header .profil-logo img {
display: block;
width: 80px;
height: 80px;
border-radius: 50%;
}
.card-body .profile-header .profil-logo::before {
content: "VIEW PRPFILE";
position: absolute;
right: 0;
top: 0;
cursor: pointer;
opacity: 0;
width: 100%;
height: 100%;
color: #eeeeee;
background: #000000;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
font-size: 0.6rem;
font-weight: 600;
text-transform: uppercase;
transition-duration: 0.15s;
}
.card-body .profile-header .profil-logo:hover::before {
opacity: 1;
}
.card-body .profile-header .badges-container {
display: flex;
flex-wrap: wrap;
justify-content: flex-end;
max-width: 220px;
background: #18191c;
border-radius: 7px;
padding: 3px;
}
.card-body .profile-header .badges-container .badge-item {
position: relative;
margin: 5px;
width: 15px;
height: 15px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
.card-body .profile-header .badges-container .badge-item img {
height: 110%;
}
.card-body .profile-header .badges-container .badge-item:hover > .tooltip {
transform: scale(1);
}
.card-body .profile-body {
background: #18191c;
border-radius: 7px;
padding: 13px;
margin-top: 40px;
}
.card-body .profile-body .username {
color: #eeeeee;
font-weight: 600;
font-size: 1.3rem;
display: flex;
flex-direction: row;
align-items: center;
}
.card-body .profile-body .username span {
color: #b9bbbe;
}
.card-body .profile-body .username .badge {
font-size: 0.65rem;
background-color: #5865f2;
text-transform: uppercase;
font-weight: 300;
width: max-content;
padding: 2px 5px;
margin-left: 5px;
border-radius: 3px;
}
.card-body .profile-body hr {
border: none;
border-top: 0.5px solid #33353b;
}
.card-body .profile-body .category-title {
color: #d6d6d6;
font-weight: 700;
text-transform: uppercase;
font-size: 0.8rem;
margin-bottom: 8px;
}
.card-body .profile-body .basic-infos {
margin-bottom: 14px;
margin-top: 12px;
}
.card-body .profile-body .basic-infos p {
color: #bdbebf;
font-size: 0.9rem;
}
.card-body .profile-body .basic-infos p a {
color: #02a5e6;
text-decoration: none;
}
.card-body .profile-body .basic-infos p a:hover {
text-decoration: underline;
}
.card-body .profile-body .basic-infos p b {
color: #ddd;
}
.card-body .profile-body .roles {
margin-bottom: 14px;
}
.card-body .profile-body .roles .roles-list {
display: flex;
flex-wrap: wrap;
}
.card-body .profile-body .roles .roles-list .role {
background: #292b2f;
color: #c4c4c4;
border-radius: 3px;
font-size: 0.75rem;
font-weight: 300;
padding: 3px 6px;
margin-right: 4px;
margin-top: 4px;
display: flex;
align-items: center;
flex-direction: row;
}
.card-body .profile-body .roles .roles-list .role .role-color {
position: relative;
width: 12px;
height: 12px;
border-radius: 50%;
margin-right: 5px;
}
.card-body .profile-body .roles .roles-list .role .role-color:hover::before {
content: "✕";
position: relative;
top: -2px;
right: 1px;
font-size: 0.65rem;
color: #f5f5f5;
background: rgba(41, 43, 47, 0);
border-radius: 50%;
width: 15px;
height: 15px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
.card-body .profile-body .roles .roles-list .role-add {
cursor: pointer;
}
.card-body .profile-body .note textarea {
border: none;
outline: none;
background: transparent;
width: 100%;
min-height: 30px;
color: #e0e0e0;
resize: none;
font-size: 0.8rem;
border-radius: 3px;
padding: 5px;
box-sizing: border-box;
scrollbar-width: none;
}
.card-body .profile-body .note textarea::-webkit-scrollbar {
display: none;
}
.card-body .profile-body .note textarea::placeholder {
font-size: 0.8rem;
}
.card-body .profile-body .note textarea:focus::placeholder {
opacity: 0;
}
.card-body .profile-body .message input {
background: transparent;
outline: none;
border: 1.2px solid #272727;
padding: 13px;
width: 100%;
border-radius: 4px;
color: #eeeeee;
margin-top: 14px;
}
.nitro-card {
position: relative;
background-image: linear-gradient(0, #000000, #a77311);
background-blend-mode: multiply;
background-color: #0000006c;
}
.nitro-card:before {
content: "";
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: -1;
margin: -5px;
border-radius: 12px;
background: linear-gradient(0, #000000, #e09200);
}
.nitro-card .card-body .profile-body {
background: linear-gradient(0, #000000, #18191c91);
}
.nitro-card .card-body .profile-header .profil-logo {
position: relative;
border-color: transparent;
z-index: 0;
}
.nitro-card .card-body .profile-header .profil-logo:after {
content: "";
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: -1;
margin: -6px;
border-radius: 50%;
background-color: rgb(74, 50, 7);
}
.nitro-card .card-body .profile-header .badges-container {
background: #18191c77;
}
.nitro-card .card-body .profile-body .roles .roles-list .role {
background: #18191c4d;
border: 1px solid #3f4149;
}
/* Media Queries */
@media screen and (max-width: 1470px) {
.card-container {
width: 1100px;
}
.card:last-child {
display: none;
}
}
@media screen and (max-width: 1150px) {
.card-container {
width: 720px;
}
.card:nth-child(3) {
display: none;
}
}
@media screen and (max-width: 770px) {
.card-container {
width: max-content;
}
.card:nth-child(2) {
display: none;
}
}
</style>
</head>
<body>
<!-- partial:index.partial.html -->
<div class="card-container">
<!-- nitro card -->
<div class="card nitro-card">
<div class="card-header">
<div
style="background: url('https://i.ibb.co/wyZxFzw/banner.gif')"
class="banner-img">
</div>
</div>
<div class="card-body">
<div class="profile-header">
<div class="profil-logo">
<img src=${logo} />
</div>
<div class="badges-container">
<div class="badge-item">
<img src="https://svgur.com/i/xLC.svg" alt="" />
<div class="tooltip tooltip-up">${text}</div>
</div>
<div class="badge-item">
<img src="https://svgur.com/i/xN3.svg" alt="" />
<div class="tooltip tooltip-up">${text1}</div>
</div>
<div class="badge-item">
<img src="https://svgur.com/i/xMp.svg" alt="" />
<div class="tooltip tooltip-up">${text2}</div>
</div>
<div class="badge-item">
<img src="https://svgur.com/i/xNq.svg" alt="" />
<div class="tooltip tooltip-up">${text3}</div>
</div>
<div class="badge-item">
<img src="https://svgur.com/i/xNK.svg" alt="" />
<div class="tooltip tooltip-up">${text4}</div>
</div>
<div class="badge-item">
<img src="https://svgur.com/i/xLD.svg" alt="" />
<div class="tooltip tooltip-up">${text5}</div>
</div>
<div class="badge-item">
<img src="https://svgur.com/i/xNF.svg" alt="" />
<div class="tooltip tooltip-up">${text6}</div>
</div>
<div class="badge-item">
<img src="https://svgur.com/i/xNe.svg" alt="" />
<div class="tooltip tooltip-up">
${text7}
</div>
</div>
</div>
</div>
<div class="profile-body">
<div class="username">
<a> ${text8} </a>
</div>
<p style="color: white;">d11b</p>
<hr/>
<div class="basic-infos">
<div class="category-title">${text9}</div>
<p>
<b>Discord</b> Verified Account User <br>Joined since &rarr; 2016<br>
<b>Discord</b> Verified Active Developer<br> since &rarr; Oct 1, 2022
</p>
</div>
<div class="basic-infos">
<div class="category-title">Member Since</div>
<div style="display: flex; align-items: center;">
<img src="https://i.ibb.co/HpbSK8B/icons8-discord-16.png" style="margin-right: 10px;">
<p style="margin: 0;">Jan 8, 2016</p>
</div>
</div>
<div class="roles">
<div class="category-title">Roles</div>
<div class="roles-list">
<div class="role">
<div class="role-color" style="background: lightyellow"></div>
<p>JavaScript</p>
</div>
<div class="role">
<div class="role-color" style="background: darkred"></div>
<p>HTML</p>
</div>
<div class="role">
<div class="role-color" style="background: darkgreen"></div>
<p>C++</p>
</div>
<div class="role">
<div class="role-color" style="background: yellow"></div>
<p>Python</p>
</div>
<div class="role">
<div class="role-color" style="background: darkblue"></div>
<p>C#</p>
</div>
<div class="role">
<div class="role-color" style="background: orange"></div>
<p>IT</p>
</div>
<div class="role role-add">
<div class="role-add-text">+</div>
</div>
</div>
</div>
<div class="note">
<div class="category-title">Note</div>
<textarea placeholder="Click to add a note"></textarea>
</div>
<div class="message">
<input id="content" type="text" placeholder=${text9}/>
</div>
<div id="message-status" style="display: none;"><b>Message Sent!</b></div>
</div>
</div>
</div>
</div>
<!-- partial -->
<script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js'></script><script src="./script.js"></script>
</body>
</html>
`;
try {
const browser = await chromium.launch({ headless: true }); // Launch Playwright Chromium browser
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.setContent(html);
const buffer = await page.screenshot({ type: 'png' });
await browser.close();
res.set('Content-Type', 'image/png');
return res.send(buffer);
} catch (error) {
console.error('Error generating PNG:', error);
res.status(500).json({ error: 'Failed to convert HTML to PNG' });
}
});
app.get('/darkcard', async (req, res) => {
const { avatar, name, job, button, desc } = req.query;
if (!(avatar || name || job || button || desc)) {
return res.status(400).json({ error: 'Missing avatar, name, job, button, desc parameters' });
}
const html = `<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>Glassmorph dark card</title>
<link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.0.2/css/bootstrap.min.css'><style>
body {
background-color: #001320;
background: -webkit-linear-gradient(90deg, 0, #1b121d 50%, #1b121d 100%);
min-height: 100vh;
font-size: 1.2rem;
letter-spacing: 1px;
}
.container {
min-height: 100vh;
}
.card-container {
position: relative;
}
.card-container::before, .card-container::after {
width: 200px;
height: 200px;
border-radius: 100%;
position: absolute;
filter: blur(20px);
z-index: 0;
}
.card-container::before {
content: "";
left: -80px;
top: -80px;
background: linear-gradient(#1845ad, #23a2f6);
}
.card-container::after {
content: "";
right: -30px;
bottom: -80px;
background: linear-gradient(to right, #ff512f, #f09819);
}
.card {
backdrop-filter: blur(16px) saturate(180%);
-webkit-backdrop-filter: blur(16px) saturate(180%);
background-color: rgba(17, 25, 40, 0.75);
border-radius: 12px;
border: 1px solid rgba(255, 255, 255, 0.125);
text-align: center;
z-index: 2;
}
.card .card-body {
padding: 1.5rem;
}
.card .card-title {
color: white;
}
.card .card-subtitle {
color: #c0f;
font-size: 1rem;
text-transform: uppercase;
letter-spacing: 2px;
font-weight: 700;
opacity: 0.7;
}
.card .card-description {
color: #9ca3af;
}
.card .card-img img {
height: 9rem;
width: 9rem;
border: 1px solid rgba(209, 213, 219, 0.3);
padding: 1rem;
}
</style>
</head>
<body>
<!-- partial:index.partial.html -->
<body>
<div class="container d-flex flex-column justify-content-center align-items center h-100">
<div class="row">
<div class="col-md-4 mx-auto">
<div class="card-container">
<div class="card">
<div class="card-body">
<div class="card-img mb-4"><img class="rounded-circle" src=${avatar}/></div>
<h2 class="card-title">${name}</h2>
<h3 class="card-subtitle">${job}</h3>
<div class="btn btn-primary mt-4">${button}</div>
<p class="card-description mb-0 mt-4">${desc}</p>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
<!-- partial -->
</body>
</html>
`;
try {
const browser = await chromium.launch({ headless: true }); // Launch Playwright Chromium browser
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.setContent(html);
const buffer = await page.screenshot({ type: 'png' });
await browser.close();
res.set('Content-Type', 'image/png');
return res.send(buffer);
} catch (error) {
console.error('Error generating PNG:', error);
res.status(500).json({ error: 'Failed to convert HTML to PNG' });
}
});
app.get('/welcome', async (req, res) => {
const { name, info, desc } = req.query;
// Ensure all required query parameters are present
if (!name || !info) {
return res.status(400).json({ error: 'Missing required parameters' });
}
// Construct HTML content dynamically based on query parameters
const html = `
<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>Course Card UI Design - #094 of #100Days100Projects</title>
<link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.10.2/css/all.min.css'>
<style>
@import url('https://fonts.googleapis.com/css?family=Muli&display=swap');
* {
box-sizing: border-box;
}
body {
background-image: linear-gradient(45deg, #7175da, #9790F2);
font-family: 'Muli', sans-serif;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
min-height: 100vh;
margin: 0;
}
.courses-container {
}
.course {
background-color: #fff;
border-radius: 10px;
box-shadow: 0 10px 10px rgba(0, 0, 0, 0.2);
display: flex;
max-width: 100%;
margin: 20px;
overflow: hidden;
width: 700px;
}
.course h6 {
opacity: 0.6;
margin: 0;
letter-spacing: 1px;
text-transform: uppercase;
}
.course h2 {
letter-spacing: 1px;
margin: 10px 0;
}
.course-preview {
background-color: #2A265F;
color: #fff;
padding: 30px;
max-width: 250px;
}
.course-preview a {
color: #fff;
display: inline-block;
font-size: 12px;
opacity: 0.6;
margin-top: 30px;
text-decoration: none;
}
.course-info {
padding: 30px;
position: relative;
width: 100%;
}
.progress-container {
position: absolute;
top: 30px;
right: 30px;
text-align: right;
width: 150px;
}
.progress {
background-color: #ddd;
border-radius: 3px;
height: 5px;
width: 100%;
}
.progress::after {
border-radius: 3px;
background-color: #2A265F;
content: '';
position: absolute;
top: 0;
left: 0;
height: 5px;
width: 66%;
}
.progress-text {
font-size: 10px;
opacity: 0.6;
letter-spacing: 1px;
}
.btn {
background-color: #2A265F;
border: 0;
border-radius: 50px;
box-shadow: 0 10px 10px rgba(0, 0, 0, 0.2);
color: #fff;
font-size: 16px;
padding: 12px 25px;
position: absolute;
bottom: 30px;
right: 30px;
letter-spacing: 1px;
}
/* SOCIAL PANEL CSS */
.social-panel-container {
position: fixed;
right: 0;
bottom: 80px;
transform: translateX(100%);
transition: transform 0.4s ease-in-out;
}
.social-panel-container.visible {
transform: translateX(-10px);
}
.social-panel {
background-color: #fff;
border-radius: 16px;
box-shadow: 0 16px 31px -17px rgba(0,31,97,0.6);
border: 5px solid #001F61;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
font-family: 'Muli';
position: relative;
height: 169px;
width: 370px;
max-width: calc(100% - 10px);
}
.social-panel button.close-btn {
border: 0;
color: #97A5CE;
cursor: pointer;
font-size: 20px;
position: absolute;
top: 5px;
right: 5px;
}
.social-panel button.close-btn:focus {
outline: none;
}
.social-panel p {
background-color: #001F61;
border-radius: 0 0 10px 10px;
color: #fff;
font-size: 14px;
line-height: 18px;
padding: 2px 17px 6px;
position: absolute;
top: 0;
left: 50%;
margin: 0;
transform: translateX(-50%);
text-align: center;
width: 235px;
}
.social-panel p i {
margin: 0 5px;
}
.social-panel p a {
color: #FF7500;
text-decoration: none;
}
.social-panel h4 {
margin: 20px 0;
color: #97A5CE;
font-family: 'Muli';
font-size: 14px;
line-height: 18px;
text-transform: uppercase;
}
.social-panel ul {
display: flex;
list-style-type: none;
padding: 0;
margin: 0;
}
.social-panel ul li {
margin: 0 10px;
}
.social-panel ul li a {
border: 1px solid #DCE1F2;
border-radius: 50%;
color: #001F61;
font-size: 20px;
display: flex;
justify-content: center;
align-items: center;
height: 50px;
width: 50px;
text-decoration: none;
}
.social-panel ul li a:hover {
border-color: #FF6A00;
box-shadow: 0 9px 12px -9px #FF6A00;
}
.floating-btn {
border-radius: 26.5px;
background-color: #001F61;
border: 1px solid #001F61;
box-shadow: 0 16px 22px -17px #03153B;
color: #fff;
cursor: pointer;
font-size: 16px;
line-height: 20px;
padding: 12px 20px;
position: fixed;
bottom: 20px;
right: 20px;
z-index: 999;
}
.floating-btn:hover {
background-color: #ffffff;
color: #001F61;
}
.floating-btn:focus {
outline: none;
}
.floating-text {
background-color: #001F61;
border-radius: 10px 10px 0 0;
color: #fff;
font-family: 'Muli';
padding: 7px 15px;
position: fixed;
bottom: 0;
left: 50%;
transform: translateX(-50%);
text-align: center;
z-index: 998;
}
.floating-text a {
color: #FF7500;
text-decoration: none;
}
@media screen and (max-width: 480px) {
.social-panel-container.visible {
transform: translateX(0px);
}
.floating-btn {
right: 10px;
}
}
</style>
</head>
<body>
<!-- partial:index.partial.html -->
<div class="courses-container">
<div class="course">
<div class="course-preview">
<h6>Course</h6>
<h2>${name}</h2>
<a href="#">View all chapters <i class="fas fa-chevron-right"></i></a>
</div>
<div class="course-info">
<div class="progress-container">
<div class="progress"></div>
<span class="progress-text">
6/9 Challenges
</span>
</div>
<h6>${info}</h6>
<h2>${desc}</h2>
<button class="btn">Continue</button>
</div>
</div>
</div>
<!-- SOCIAL PANEL HTML -->
<div class="social-panel-container">
<div class="social-panel">
<p>Created with <i class="fa fa-heart"></i> by
<a target="_blank" href="https://florin-pop.com">Florin Pop</a></p>
<button class="close-btn"><i class="fas fa-times"></i></button>
<h4>Get in touch on</h4>
<ul>
<li>
<a href="https://www.patreon.com/florinpop17" target="_blank">
<i class="fab fa-discord"></i>
</a>
</li>
<li>
<a href="https://twitter.com/florinpop1705" target="_blank">
<i class="fab fa-twitter"></i>
</a>
</li>
<li>
<a href="https://linkedin.com/in/florinpop17" target="_blank">
<i class="fab fa-linkedin"></i>
</a>
</li>
<li>
<a href="https://facebook.com/florinpop17" target="_blank">
<i class="fab fa-facebook"></i>
</a>
</li>
<li>
<a href="https://instagram.com/florinpop17" target="_blank">
<i class="fab fa-instagram"></i>
</a>
</li>
</ul>
</div>
</div>
<button class="floating-btn">
Get in Touch
</button>
<div class="floating-text">
Part of <a href="https://florin-pop.com/blog/2019/09/100-days-100-projects" target="_blank">#100Days100Projects</a>
</div>
<!-- partial -->
<script>
// INSERT JS HERE
// SOCIAL PANEL JS
const floating_btn = document.querySelector('.floating-btn');
const close_btn = document.querySelector('.close-btn');
const social_panel_container = document.querySelector('.social-panel-container');
floating_btn.addEventListener('click', () => {
social_panel_container.classList.toggle('visible')
});
close_btn.addEventListener('click', () => {
social_panel_container.classList.remove('visible')
});
</script>
</body>
</html>`;
try {
const browser = await chromium.launch({ headless: true }); // Launch Playwright Chromium browser
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.setContent(html);
const buffer = await page.screenshot({ type: 'png' });
await browser.close();
res.set('Content-Type', 'image/png');
return res.send(buffer);
} catch (error) {
console.error('Error generating PNG:', error);
res.status(500).json({ error: 'Failed to convert HTML to PNG' });
}
});
const PORT = process.env.PORT || 7860;
app.listen(PORT, async () => {
console.log(`Server running on port ${PORT}`);
});
process.on('SIGINT', async () => {
process.exit(0);
});