CoolFace
Apppublic

RentCalc-validation/RentCalc.com

sourceHugging Faceupdated 8mo agoView on Hugging Face
1likes
index.js186 linesDownload Raw Back to server
1import express from 'express';2import cors from 'cors';3import nodemailer from 'nodemailer';4import dotenv from 'dotenv';5import { createClient } from '@supabase/supabase-js';6 7dotenv.config();8 9const app = express();10const PORT = process.env.PORT || 10000;11 12// Supabase Configuration13const supabaseUrl = process.env.SUPABASE_URL;14const supabaseKey = process.env.SUPABASE_KEY;15const supabase = createClient(supabaseUrl, supabaseKey);16 17// Email Transporter - Matching friend's working config18const transporter = nodemailer.createTransport({19    service: 'gmail',20    host: process.env.SMTP_HOST || 'smtp.gmail.com',21    port: parseInt(process.env.SMTP_PORT) || 465,22    secure: true,23    auth: {24        user: process.env.EMAIL_USER,25        pass: process.env.EMAIL_PASS,26    },27    tls: {28        rejectUnauthorized: false29    }30});31 32// Neobrutalist Email Template33const getEmailTemplate = (name) => `34    <!DOCTYPE html>35    <html>36    <head>37        <meta charset="utf-8">38        <meta name="viewport" content="width=device-width, initial-scale=1.0">39        <link href="https://fonts.googleapis.com/css2?family=Bungee&family=Lexend+Mega:wght@900&family=JetBrains+Mono:wght@700&display=swap" rel="stylesheet">40        <style>41            .heading-font { font-family: 'Lexend Mega', 'Arial Black', sans-serif !important; }42            .code-font { font-family: 'JetBrains Mono', 'Courier New', monospace !important; }43        </style>44    </head>45    <body style="margin: 0; padding: 0; background-color: #F8F9FA; font-family: 'Helvetica', 'Arial', sans-serif;">46        <table width="100%" border="0" cellspacing="0" cellpadding="0" style="background-color: #F8F9FA; padding: 40px 10px;">47            <tr>48                <td align="center">49                    <table width="100%" border="0" cellspacing="0" cellpadding="0" style="background-color: #ffffff; border: 4px solid #1A1A1A; box-shadow: 12px 12px 0px 0px #1A1A1A; max-width: 600px; width: 100%;">50                        <tr>51                            <td style="padding: 40px 20px 20px 20px;">52                                <img src="https://raw.githubusercontent.com/gmahesh-star/rentcalc-validation-website/main/public/brand/logo.png" alt="RentCalc Logo" width="180" style="display: block; border: 0; max-width: 80%;">53                            </td>54                        </tr>55                        <tr>56                            <td style="padding: 0 20px 20px 20px;">57                                <h1 class="heading-font" style="margin: 0; font-size: 28px; font-weight: 900; color: #1A1A1A; line-height: 1.2; text-transform: uppercase;">58                                    HEYY ${name.toUpperCase() || 'THERE'}!59                                </h1>60                            </td>61                        </tr>62                        <tr>63                            <td style="padding: 0 20px;">64                                <div style="height: 4px; background-color: #1A1A1A; width: 60px; margin-bottom: 25px;"></div>65                            </td>66                        </tr>67                        <tr>68                            <td style="padding: 0 20px 25px 20px;">69                                <p style="margin: 0; font-family: 'Arial', sans-serif; font-size: 16px; font-weight: 700; color: #1A1A1A; line-height: 1.6;">70                                    Thanks for checking out RentCalc. Honestly, I really appreciate you taking a few seconds to vote. It helps me figure out if this is actually something worth building for us.71                                </p>72                            </td>73                        </tr>74                        <tr>75                            <td style="padding: 0 20px 30px 20px;">76                                <table width="100%" border="0" cellspacing="0" cellpadding="0" style="background-color: #00EAD3; border: 4px solid #1A1A1A; box-shadow: 6px 6px 0px 0px #1A1A1A;">77                                    <tr>78                                        <td style="padding: 20px;">79                                            <p class="heading-font" style="margin: 0 0 10px 0; font-size: 18px; font-weight: 900; text-transform: uppercase; color: #1A1A1A;">SO, WHAT'S THE PLAN?</p>80                                            <p style="margin: 0; font-family: 'Arial', sans-serif; font-size: 15px; font-weight: 700; color: #1A1A1A; line-height: 1.5;">81                                                I'm just talking to more people to see how many of us actually face these rental/resale struggles. If it's a big enough need, I'll get to work. You'll be the first one I'll ping when I have something real to show.82                                            </p>83                                        </td>84                                    </tr>85                                </table>86                            </td>87                        </tr>88                        <tr>89                            <td style="padding: 0 20px 30px 20px;">90                                <div style="background-color: #1A1A1A; color: #ffffff; padding: 12px 20px; display: inline-block;">91                                    <p class="code-font" style="margin: 0; font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: 1px;">92                                        NO CORPORATE FLUFF. JUST ME BUILDING FOR US.93                                    </p>94                                </div>95                            </td>96                        </tr>97                        <tr>98                            <td style="padding: 25px 20px; border-top: 4px solid #1A1A1A;">99                                <p class="heading-font" style="margin: 0; font-size: 18px; font-weight: 900; color: #1A1A1A; text-transform: uppercase;">100                                    — Mahesh101                                </p>102                                <p class="code-font" style="margin: 5px 0 0 0; font-size: 12px; font-weight: 700; color: #666666; text-transform: uppercase;">103                                    Creator, RentCalc104                                </p>105                            </td>106                        </tr>107                    </table>108                    <p class="code-font" style="margin-top: 25px; font-size: 10px; font-weight: 700; color: #666666; text-transform: uppercase; letter-spacing: 1px;">109                        No spam. No pressure. Just honest building.110                    </p>111                </td>112            </tr>113        </table>114    </body>115    </html>116`;117 118// Allow requests from Hugging Face119app.use(cors());120app.use(express.json());121 122// Health check endpoint123app.get('/', (req, res) => {124    res.json({ status: 'RentCalc API is running!' });125});126 127app.post('/api/responses', async (req, res) => {128    try {129        const { pollData } = req.body;130 131        // Save to Supabase132        const { data, error: sbError } = await supabase133            .from('responses')134            .insert([{ poll_data: pollData }]);135 136        if (sbError) throw sbError;137 138        // Check if user wants notification and provided an email139        const wantsNotify = pollData?.notificationChoice === 'Yes (share contact)';140        const contactEmail = pollData?.email;141        const studentName = pollData?.name?.trim() || 'Student';142 143        if (wantsNotify && contactEmail) {144            const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;145            if (emailRegex.test(contactEmail.trim())) {146                const mailOptions = {147                    from: `"Mahesh from RentCalc" <${process.env.EMAIL_USER}>`,148                    to: contactEmail.trim(),149                    subject: `Yo ${studentName}! Thanks for the RentCalc feedback`,150                    html: getEmailTemplate(studentName)151                };152 153                transporter.sendMail(mailOptions, (error, info) => {154                    if (error) {155                        console.error('Email sending failed:', error);156                    } else {157                        console.log('Email sent successfully to:', contactEmail);158                    }159                });160            }161        }162 163        res.status(201).json({ message: 'Response saved successfully' });164    } catch (error) {165        console.error('Error saving response:', error);166        res.status(500).json({ error: 'Failed' });167    }168});169 170app.get('/api/stats', async (req, res) => {171    try {172        const { count, error } = await supabase173            .from('responses')174            .select('*', { count: 'exact', head: true });175 176        if (error) throw error;177        res.json({ count: count || 0 });178    } catch (error) {179        res.status(500).json({ error: 'Failed' });180    }181});182 183app.listen(PORT, '0.0.0.0', () => {184    console.log(`RentCalc API running on port ${PORT}`);185});186