This project is an advanced Express.js application that integrates MySQL, session management, OTP-based authentication, file handling, and dynamic page rendering. The application is built to handle domain-specific configurations and communicate with external services via Socket.IO.
- Features
- Installation
- Usage
- Environment Variables
- CORS Configuration
- Running the Application
- License
- Session Management: Securely manages user sessions using
express-session
with MySQL as the session store. - Domain-Based Configuration: Middleware to fetch, cache, and utilize configurations based on the domain.
- OTP Authentication: Secure OTP generation, email sending, and verification for login authentication.
- File Handling: Supports secure file uploads with encryption for sensitive data.
- Dynamic Page Rendering: Renders pages dynamically based on domain-specific settings stored in the database.
- Socket.IO Integration: Connects to a Socket.IO server for real-time data fetching and interaction.
- Email Notifications: Sends OTPs and other notifications via a configured email service (e.g., Gmail SMTP).
- CORS Support: Allows cross-origin requests with configurable CORS settings.
To set up the application locally, follow these steps:
git clone https://github.com/your-repo-name.git
cd your-repo-name
domainCheck
Middleware:- Purpose: Fetches domain-specific configurations from the database and stores them in the session.
- How it Works:
- It checks the
x-forwarded-host
header in the request to determine the domain. - If the configuration for the domain is already stored in the session, it uses that; otherwise, it fetches the configuration from the
domain_config
table in the database. - If no configuration is found for the domain, a 404 error is returned.
- Example:
const domainCheck = async (req, res, next) => { const domain = req.headers['x-forwarded-host']; if (req.session && req.session.config && req.session.config.domain === domain) { req.config = req.session.config; return next(); } try { const [rows] = await pool.query('SELECT * FROM domain_config WHERE domain = ?', [domain]); if (rows.length > 0) { req.config = rows[0]; req.session.config = req.config; next(); } else { res.status(404).send('Configuration not found for this domain.'); } } catch (error) { res.status(500).send('Internal Server Error: Unable to fetch configuration.'); } };
- It checks the
-
/
(GET):- Description: Renders the login page. If the user is already authenticated, redirects to the dashboard.
- Example Usage:
app.get('/', (req, res) => { if (req.session.user) { res.redirect('/dashboard'); } else { res.render('login', { logo: req.config.logo_url }); } });
-
/login
(POST):- Description: Handles user login by generating and sending an OTP to the user's email.
- Process:
- Receives the username (email).
- Generates a 6-digit OTP and stores it in a map for verification.
- Sends the OTP via email and a webhook.
- Redirects the user to an OTP entry page.
- Example Usage:
app.post('/login', async (req, res) => { const { username } = req.body; const otp = generateOTP(); otpMap.set(username, otp); const emailStatus = await sendOTPEmailAndTriggerWebhook(username, otp); if (emailStatus) { res.render('otp', { username }); } else { res.send('Error occurred while sending OTP.'); } });
-
/verifyOTP
(POST):- Description: Verifies the OTP entered by the user. If the OTP is correct, the user is redirected to the dashboard.
- Process:
- Receives the OTP and username.
- Verifies if the OTP matches the one stored in the map.
- If valid, the session is updated, and the user is redirected to the dashboard.
- Example Usage:
app.post('/verifyOTP', (req, res) => { const { username, otp } = req.body; if (otpMap.has(username) && otpMap.get(username) === otp) { req.session.user = username; otpMap.delete(username); res.redirect('/dashboard'); } else { res.redirect('/?error=Invalid OTP'); } });
-
/dashboard
(GET):- Description: Displays the user dashboard with domain-specific configurations.
- Process:
- The user's session is checked to ensure they are logged in.
- Real-time data is fetched using Socket.IO and rendered on the dashboard.
- Example Usage:
app.get('/dashboard', sessionCheck, async (req, res) => { socket.emit('contact', { query1: req.session.user, query2: req.config.location_id, query3: 10, }); socket.once('searchResult', async (data) => { if (data.error) { res.status(500).send('An error occurred.'); } else { const htmlContent = generateHtmlContent(data.contacts); res.render('dashboard', { title: 'Dashboard', user: req.session.user, logo: req.config.logo_url, contacts: htmlContent, }); } }); });
-
/company
(GET):- Description: Displays detailed information about a specific company, including downloadable files and other details.
- Process:
- The company ID is passed as a query parameter.
- The company details are fetched using Socket.IO.
- The data is then rendered dynamically on the page.
- Example Usage:
app.get('/company', sessionCheck, async (req, res) => { const companyId = req.query.id; socket.emit('contactid', { query1: companyId, query2: req.config.location_id, }); socket.once('searchResult', async (data) => { const htmlContent = generateCompanyHtml(data.contact); res.render('company', { title: `${data.contact.companyName} | Company`, user: req.session.user, contacts: htmlContent, }); }); });
-
/single
(GET):- Description: Displays visa-related information for a specific company.
- Process:
- The visa data is fetched based on the company ID using Socket.IO.
- The visa information is rendered dynamically.
- Example Usage:
app.get('/single', sessionCheck, async (req, res) => { const companyId = req.query.id; socket.emit('contactid', { query1: companyId, query2: req.config.v_location_id, }); socket.once('searchResult', async (data) => { const htmlContent = generateVisaHtml(data.contact); res.render('single', { title: `${data.contact.companyName} | Visa`, user: req.session.user, contacts: htmlContent, }); }); });
-
/add-page
(GET/POST):- Description: Admin interface to add new pages. Pages are tied to specific domains and can be managed through this route.
- GET: Displays a form to add a new page.
- POST: Handles form submission and adds a new page to the database.
- Example Usage:
app.get('/add-page', sessionCheck, async (req, res) => { const allPages = await fetchAllPages(); res.render('add-page', { user: req.session.user, pages: allPages, }); }); app.post('/add-page', express.json(), sessionCheck, (req, res) => { const { domain, icon, title, featureImageUrl, landingPageUrl } = req.body; const sql = 'INSERT INTO pages (domain, icon, title, feature_image_url, landing_page_url) VALUES (?, ?, ?, ?, ?)'; db.query(sql, [domain, icon, title, featureImageUrl, landingPageUrl], (err, result) => { if (err) { return res.status(500).send('Error adding new page'); } res.redirect('/add-page'); }); });
-
/edit-addonpage/:id
(GET/POST):- Description: Edit an existing page by its ID. The form is pre-filled with the current page data.
- GET: Fetches the existing page data and displays it in a form for editing.
- POST: Handles form submission and updates the page in the database.
- Example Usage:
app.get('/edit-addonpage/:id', sessionCheck, async (req, res) => { const pageId = req.params.id; const pageData = await fetchPageById(pageId); res.render('edit-page', { user: req.session.user, page: pageData, }); }); app.post('/edit-addonpage/:id', express.json(), sessionCheck, (req, res) => { const { id, domain, icon, title, featureImageUrl, landingPageUrl } = req.body; const sql = 'UPDATE pages SET domain = ?, icon = ?, title = ?, feature_image_url = ?, landing_page_url = ? WHERE id = ?'; db.query(sql, [domain, icon, title, featureImageUrl, landingPageUrl, id], (err, result) => { if (err) { return res.status(500).send('Error updating page'); } res.redirect('/add-page'); }); });
-
/addon/:page
(GET):- Description: Renders addon pages dynamically based on the domain and user session.
- Process:
- The page URL is decrypted and rendered with additional parameters for the user.
- Example Usage:
app.get('/addon/:page', sessionCheck, async (req, res) => { const addonPageUrl = decrypt(req.params.page, "667766"); const userParams = { full_name: req.session.namo, email: req.session.user, }; const updatedUrl = `${addonPageUrl}?${new URLSearchParams(userParams).toString()}`; res.render('addon', { user: req.session.user, addonPageUrl: updatedUrl, }); });