const fs = require('fs'); const chokidar = require('chokidar'); const Logger = require('./Logger'); const log = new Logger('HtmlInjector'); class HtmlInjector { constructor(filesPath, config) { this.filesPath = filesPath; // Array of file paths to cache this.cache = {}; // Object to store cached files this.config = config; // Configuration containing metadata (OG, title, etc.) this.injectData = this.getInjectData(); // Initialize dynamic injection data this.watcher = null; // File watcher instance this.preloadPages(filesPath); // Preload pages at startup this.watchFiles(filesPath); // Watch files for changes log.info('filesPath cached', this.filesPath); } // Function to get dynamic data for injection (e.g., OG data, title, etc.) getInjectData() { return { OG_TYPE: this.config?.og?.type || 'app-webrtc', OG_SITE_NAME: this.config?.og?.siteName || 'MiroTalk SFU', OG_TITLE: this.config?.og?.title || 'Click the link to make a call.', OG_DESCRIPTION: this.config?.og?.description || 'MiroTalk SFU calling provides real-time video calls, messaging and screen sharing.', OG_IMAGE: this.config?.og?.image || 'https://sfu.mirotalk.com/images/mirotalksfu.png', OG_URL: this.config?.og?.url || 'https://sfu.mirotalk.com', // Add more data here as needed with fallbacks }; } // Function to load a file into the cache loadFileToCache(filePath) { try { const content = fs.readFileSync(filePath, 'utf-8'); this.cache[filePath] = content; // Store the content in cache } catch (err) { log.error(`Error reading file: ${filePath}`, err); } } // Function to preload pages into the cache preloadPages(filePaths) { filePaths.forEach((filePath) => this.loadFileToCache(filePath)); } // Function to watch files for changes using chokidar watchFiles(filePaths) { if (this.watcher) { this.watcher.close(); // Close existing watcher if any } this.watcher = chokidar.watch(filePaths, { persistent: true, ignoreInitial: true, // Ignore initial 'add' events }); this.watcher .on('change', (filePath) => { log.debug(`File changed: ${filePath}`); this.loadFileToCache(filePath); log.debug(`Reloaded file ${filePath} into cache`); }) .on('error', (error) => { log.error(`Watcher error: ${error.message}`); }); } // Function to inject dynamic data (e.g., OG, TITLE, etc.) into a given file injectHtml(filePath, res) { // return res.send(this.cache[filePath]); if (!this.cache[filePath]) { log.error(`File not cached: ${filePath}`); if (!res.headersSent) { return res.status(500).send('Server Error'); } return; } try { // Replace placeholders with dynamic data (OG, TITLE, etc.) const modifiedHTML = this.cache[filePath].replace( /{{(OG_[A-Z_]+)}}/g, (_, key) => this.injectData[key] || '' ); if (!res.headersSent) { res.send(modifiedHTML); } } catch (error) { log.error('Error injecting HTML data:', error); if (!res.headersSent) { res.status(500).send('Server Error'); } } } // Cleanup watcher when the instance is no longer needed cleanup() { if (this.watcher) { this.watcher.close(); } } } module.exports = HtmlInjector;