mirror of
https://github.com/SrIzan10/lynn-come-out.git
synced 2026-06-06 00:56:57 +00:00
431 lines
11 KiB
JavaScript
431 lines
11 KiB
JavaScript
const express = require('express');
|
|
const session = require('express-session');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
require('dotenv').config();
|
|
|
|
const app = express();
|
|
const PORT = process.env.PORT || 3000;
|
|
|
|
// Middleware
|
|
app.use(express.json());
|
|
app.use(express.urlencoded({ extended: true }));
|
|
app.use(session({
|
|
secret: process.env.SESSION_SECRET || 'super-secret-key-change-this',
|
|
resave: false,
|
|
saveUninitialized: false,
|
|
cookie: { secure: false } // Set to true if using HTTPS
|
|
}));
|
|
|
|
// Serve static files
|
|
app.use(express.static('public'));
|
|
|
|
// Data file path
|
|
const SIGNATURES_FILE = path.join(__dirname, 'signatures.json');
|
|
|
|
// Initialize signatures file if it doesn't exist
|
|
if (!fs.existsSync(SIGNATURES_FILE)) {
|
|
fs.writeFileSync(SIGNATURES_FILE, JSON.stringify([]));
|
|
}
|
|
|
|
// Helper function to read signatures
|
|
function getSignatures() {
|
|
const data = fs.readFileSync(SIGNATURES_FILE, 'utf8');
|
|
return JSON.parse(data);
|
|
}
|
|
|
|
// Helper function to save signatures
|
|
function saveSignatures(signatures) {
|
|
fs.writeFileSync(SIGNATURES_FILE, JSON.stringify(signatures, null, 2));
|
|
}
|
|
|
|
// Routes
|
|
app.get('/', (req, res) => {
|
|
const signatures = getSignatures();
|
|
const isSignedIn = req.session.user ? true : false;
|
|
const userName = req.session.user ? req.session.user.name : '';
|
|
const userImage = req.session.user ? req.session.user.image : '';
|
|
const hasUserSigned = req.session.user ? signatures.some(s => s.userId === req.session.user.id) : false;
|
|
|
|
res.send(`
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>LYNN COME OUT!!!</title>
|
|
<style>
|
|
@keyframes rainbow {
|
|
0% { background-position: 0% 50%; }
|
|
50% { background-position: 100% 50%; }
|
|
100% { background-position: 0% 50%; }
|
|
}
|
|
|
|
@keyframes spin {
|
|
0% { transform: rotate(0deg); }
|
|
100% { transform: rotate(360deg); }
|
|
}
|
|
|
|
@keyframes bounce {
|
|
0%, 100% { transform: translateY(0); }
|
|
50% { transform: translateY(-20px); }
|
|
}
|
|
|
|
@keyframes blink {
|
|
0%, 50%, 100% { opacity: 1; }
|
|
25%, 75% { opacity: 0; }
|
|
}
|
|
|
|
body {
|
|
font-family: 'Comic Sans MS', cursive, sans-serif;
|
|
background: linear-gradient(45deg, #ff00ff, #00ffff, #ffff00, #ff00ff);
|
|
background-size: 400% 400%;
|
|
animation: rainbow 3s ease infinite;
|
|
margin: 0;
|
|
padding: 20px;
|
|
color: #ff0000;
|
|
text-shadow: 2px 2px #00ff00;
|
|
overflow-x: hidden;
|
|
}
|
|
|
|
.container {
|
|
max-width: 900px;
|
|
margin: 0 auto;
|
|
background-color: #ffff00;
|
|
border: 10px dashed #ff00ff;
|
|
padding: 30px;
|
|
box-shadow: 0 0 50px rgba(255, 0, 255, 0.8);
|
|
}
|
|
|
|
h1 {
|
|
font-size: 72px;
|
|
text-align: center;
|
|
animation: bounce 1s infinite;
|
|
color: #ff0000;
|
|
text-shadow: 5px 5px #0000ff, 10px 10px #00ff00;
|
|
text-decoration: underline wavy #ff00ff;
|
|
margin: 0;
|
|
padding: 20px;
|
|
background: repeating-linear-gradient(
|
|
45deg,
|
|
#ff00ff,
|
|
#ff00ff 10px,
|
|
#00ffff 10px,
|
|
#00ffff 20px
|
|
);
|
|
}
|
|
|
|
.subtitle {
|
|
font-size: 36px;
|
|
text-align: center;
|
|
animation: blink 2s infinite;
|
|
color: #00ff00;
|
|
text-shadow: 3px 3px #ff0000;
|
|
margin: 20px 0;
|
|
}
|
|
|
|
.sign-in-btn {
|
|
display: block;
|
|
margin: 30px auto;
|
|
padding: 20px 40px;
|
|
font-size: 28px;
|
|
font-family: 'Comic Sans MS', cursive, sans-serif;
|
|
background-color: #ff00ff;
|
|
color: #ffff00;
|
|
border: 5px solid #00ff00;
|
|
cursor: pointer;
|
|
animation: spin 3s linear infinite;
|
|
text-decoration: none;
|
|
text-align: center;
|
|
font-weight: bold;
|
|
box-shadow: 0 0 20px rgba(255, 0, 0, 0.8);
|
|
}
|
|
|
|
.sign-in-btn:hover {
|
|
background-color: #00ffff;
|
|
color: #ff0000;
|
|
border-color: #ff00ff;
|
|
transform: scale(1.2);
|
|
}
|
|
|
|
.signature-btn {
|
|
display: block;
|
|
margin: 30px auto;
|
|
padding: 20px 40px;
|
|
font-size: 32px;
|
|
font-family: 'Comic Sans MS', cursive, sans-serif;
|
|
background-color: #00ff00;
|
|
color: #ff0000;
|
|
border: 8px double #0000ff;
|
|
cursor: pointer;
|
|
text-decoration: none;
|
|
text-align: center;
|
|
font-weight: bold;
|
|
box-shadow: 0 0 30px rgba(0, 255, 0, 0.9);
|
|
animation: bounce 0.5s infinite;
|
|
}
|
|
|
|
.signature-btn:hover {
|
|
background-color: #ff0000;
|
|
color: #ffff00;
|
|
transform: rotate(5deg) scale(1.1);
|
|
}
|
|
|
|
.signatures {
|
|
margin-top: 40px;
|
|
padding: 20px;
|
|
background-color: #00ffff;
|
|
border: 5px solid #ff0000;
|
|
}
|
|
|
|
.signatures h2 {
|
|
color: #ff00ff;
|
|
text-shadow: 2px 2px #ffff00;
|
|
font-size: 48px;
|
|
animation: blink 1.5s infinite;
|
|
}
|
|
|
|
.signature-item {
|
|
background-color: #ffff00;
|
|
border: 3px dotted #ff00ff;
|
|
padding: 15px;
|
|
margin: 10px 0;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 15px;
|
|
animation: bounce 2s infinite;
|
|
}
|
|
|
|
.signature-item img {
|
|
width: 50px;
|
|
height: 50px;
|
|
border-radius: 50%;
|
|
border: 3px solid #ff0000;
|
|
animation: spin 5s linear infinite;
|
|
}
|
|
|
|
.signature-item .name {
|
|
font-size: 24px;
|
|
color: #0000ff;
|
|
text-shadow: 1px 1px #ff00ff;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.signature-item .timestamp {
|
|
font-size: 16px;
|
|
color: #ff0000;
|
|
margin-left: auto;
|
|
}
|
|
|
|
.user-info {
|
|
background-color: #ff00ff;
|
|
color: #ffff00;
|
|
padding: 20px;
|
|
margin: 20px 0;
|
|
border: 5px ridge #00ff00;
|
|
text-align: center;
|
|
font-size: 24px;
|
|
font-weight: bold;
|
|
text-shadow: 2px 2px #ff0000;
|
|
}
|
|
|
|
.user-info img {
|
|
width: 80px;
|
|
height: 80px;
|
|
border-radius: 50%;
|
|
border: 5px solid #00ffff;
|
|
animation: spin 4s linear infinite;
|
|
margin: 10px;
|
|
}
|
|
|
|
.logout-btn {
|
|
display: inline-block;
|
|
margin: 10px;
|
|
padding: 10px 20px;
|
|
font-size: 18px;
|
|
font-family: 'Comic Sans MS', cursive, sans-serif;
|
|
background-color: #ff0000;
|
|
color: #ffff00;
|
|
border: 3px solid #00ff00;
|
|
cursor: pointer;
|
|
text-decoration: none;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.count {
|
|
font-size: 48px;
|
|
text-align: center;
|
|
color: #ff00ff;
|
|
text-shadow: 3px 3px #00ffff;
|
|
margin: 30px 0;
|
|
animation: bounce 1.5s infinite;
|
|
background-color: #00ff00;
|
|
padding: 20px;
|
|
border: 5px solid #ff0000;
|
|
}
|
|
|
|
marquee {
|
|
font-size: 32px;
|
|
color: #ff0000;
|
|
background-color: #ffff00;
|
|
padding: 10px;
|
|
border: 3px solid #ff00ff;
|
|
margin: 20px 0;
|
|
}
|
|
|
|
.already-signed {
|
|
background-color: #ff0000;
|
|
color: #ffff00;
|
|
padding: 20px;
|
|
text-align: center;
|
|
font-size: 24px;
|
|
border: 5px solid #00ff00;
|
|
margin: 20px 0;
|
|
animation: blink 1s infinite;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<h1>🏳️⚧️ LYNN COME OUT!!! 🏳️⚧️</h1>
|
|
<marquee behavior="scroll" direction="left">✨ LYNN COME OUT ✨ LYNN COME OUT ✨ LYNN COME OUT ✨</marquee>
|
|
<div class="subtitle">💖 Sign the Petition NOW!!! 💖</div>
|
|
|
|
${isSignedIn ? `
|
|
<div class="user-info">
|
|
<img src="${userImage}" alt="User">
|
|
<div>Welcome, ${userName}! 🎉</div>
|
|
<a href="/logout" class="logout-btn">Sign Out</a>
|
|
</div>
|
|
|
|
${hasUserSigned ? `
|
|
<div class="already-signed">
|
|
✅ YOU ALREADY SIGNED! THANK YOU! ✅
|
|
</div>
|
|
` : `
|
|
<form method="POST" action="/sign">
|
|
<button type="submit" class="signature-btn">
|
|
✍️ SIGN THE PETITION ✍️
|
|
</button>
|
|
</form>
|
|
`}
|
|
` : `
|
|
<a href="/auth/slack" class="sign-in-btn">
|
|
🔐 SIGN IN WITH SLACK 🔐
|
|
</a>
|
|
`}
|
|
|
|
<div class="count">
|
|
📝 TOTAL SIGNATURES: ${signatures.length} 📝
|
|
</div>
|
|
|
|
<div class="signatures">
|
|
<h2>💫 SUPPORTERS 💫</h2>
|
|
${signatures.length === 0 ? '<p style="font-size: 24px; text-align: center; color: #ff0000;">No signatures yet! Be the first! 🎯</p>' : ''}
|
|
${signatures.map(sig => `
|
|
<div class="signature-item">
|
|
<img src="${sig.userImage}" alt="${sig.userName}">
|
|
<span class="name">${sig.userName}</span>
|
|
<span class="timestamp">${new Date(sig.timestamp).toLocaleString()}</span>
|
|
</div>
|
|
`).join('')}
|
|
</div>
|
|
|
|
<marquee behavior="scroll" direction="right">🏳️⚧️ LYNN WE SUPPORT YOU 🏳️⚧️ COME OUT COME OUT WHEREVER YOU ARE 🏳️⚧️</marquee>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
`);
|
|
});
|
|
|
|
// Slack OAuth - Start
|
|
app.get('/auth/slack', (req, res) => {
|
|
const clientId = process.env.SLACK_CLIENT_ID;
|
|
const redirectUri = process.env.SLACK_REDIRECT_URI;
|
|
const scopes = 'identity.basic,identity.avatar';
|
|
|
|
if (!clientId) {
|
|
return res.send('<h1>Error: SLACK_CLIENT_ID not configured</h1><p>Please set up your .env file with Slack credentials.</p>');
|
|
}
|
|
|
|
const authUrl = `https://slack.com/oauth/authorize?client_id=${clientId}&scope=${scopes}&redirect_uri=${encodeURIComponent(redirectUri)}`;
|
|
res.redirect(authUrl);
|
|
});
|
|
|
|
// Slack OAuth - Callback
|
|
app.get('/auth/slack/callback', async (req, res) => {
|
|
const code = req.query.code;
|
|
const clientId = process.env.SLACK_CLIENT_ID;
|
|
const clientSecret = process.env.SLACK_CLIENT_SECRET;
|
|
const redirectUri = process.env.SLACK_REDIRECT_URI;
|
|
|
|
if (!code) {
|
|
return res.send('<h1>Error: No authorization code received</h1><a href="/">Go back</a>');
|
|
}
|
|
|
|
try {
|
|
// Exchange code for access token
|
|
const tokenUrl = 'https://slack.com/api/oauth.access';
|
|
const params = new URLSearchParams({
|
|
client_id: clientId,
|
|
client_secret: clientSecret,
|
|
code: code,
|
|
redirect_uri: redirectUri
|
|
});
|
|
|
|
const response = await fetch(`${tokenUrl}?${params.toString()}`);
|
|
const data = await response.json();
|
|
|
|
if (data.ok) {
|
|
// Store user info in session
|
|
req.session.user = {
|
|
id: data.user_id,
|
|
name: data.user.name,
|
|
image: data.user.image_48 || data.user.image_72 || 'https://via.placeholder.com/48'
|
|
};
|
|
res.redirect('/');
|
|
} else {
|
|
res.send(`<h1>Error: ${data.error}</h1><p>Failed to authenticate with Slack</p><a href="/">Go back</a>`);
|
|
}
|
|
} catch (error) {
|
|
res.send(`<h1>Error: ${error.message}</h1><a href="/">Go back</a>`);
|
|
}
|
|
});
|
|
|
|
// Sign the petition
|
|
app.post('/sign', (req, res) => {
|
|
if (!req.session.user) {
|
|
return res.redirect('/');
|
|
}
|
|
|
|
const signatures = getSignatures();
|
|
|
|
// Check if user already signed
|
|
if (signatures.some(s => s.userId === req.session.user.id)) {
|
|
return res.redirect('/');
|
|
}
|
|
|
|
// Add signature
|
|
signatures.push({
|
|
userId: req.session.user.id,
|
|
userName: req.session.user.name,
|
|
userImage: req.session.user.image,
|
|
timestamp: new Date().toISOString()
|
|
});
|
|
|
|
saveSignatures(signatures);
|
|
res.redirect('/');
|
|
});
|
|
|
|
// Logout
|
|
app.get('/logout', (req, res) => {
|
|
req.session.destroy();
|
|
res.redirect('/');
|
|
});
|
|
|
|
// Start server
|
|
app.listen(PORT, () => {
|
|
console.log(`Server running on http://localhost:${PORT}`);
|
|
console.log('Make sure to set up your .env file with Slack credentials!');
|
|
});
|