diff --git a/.env.example b/.env.example index 2edf134..a0f6846 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,6 @@ # Server settings PORT=3000 -DASHBOARD_HOST=ipfs.hnshosting.au +DASHBOARD_HOST=ipfs.hnsproxy.au # IPFS settings IPFS_GATEWAY=https://ipfs.io diff --git a/server.js b/server.js index ec6409a..817266e 100644 --- a/server.js +++ b/server.js @@ -14,6 +14,18 @@ app.use(cors()); app.use(morgan('dev')); app.use(express.json()); +// Add a request logger middleware at the very beginning +app.use((req, res, next) => { + console.log('\n----- NEW REQUEST -----'); + console.log(`${req.method} ${req.url}`); + console.log(`Headers: ${JSON.stringify({ + host: req.get('host'), + referer: req.get('referer'), + 'user-agent': req.get('user-agent') + }, null, 2)}`); + next(); +}); + // Define the main dashboard host // This should be configurable via environment variable const DASHBOARD_HOST = process.env.DASHBOARD_HOST || '127.0.0.1:3000'; @@ -31,7 +43,53 @@ const RESERVED_PATHS = [ 'favicon.ico' ]; -// Serve static files +// Helper function to normalize host strings for comparison +function normalizeHost(host) { + if (!host) return ''; + // Remove port if present and convert to lowercase + const normalized = host.split(':')[0].toLowerCase(); + console.log(`Normalizing host: ${host} -> ${normalized}`); + return normalized; +} + +// IMPORTANT: Move direct domain access middleware before static file serving +// Middleware to check if request is for direct domain access +app.use(async (req, res, next) => { + const host = req.get('host'); + const normalizedHost = normalizeHost(host); + const normalizedDashboardHost = normalizeHost(DASHBOARD_HOST); + + console.log(`[HOST CHECK] Request host: ${host}, Normalized: ${normalizedHost}, Dashboard: ${normalizedDashboardHost}`); + + // Special handling for curl requests with Host header + if (normalizedHost !== normalizedDashboardHost) { + console.log(`[HOST CHECK] Host ${normalizedHost} doesn't match dashboard ${normalizedDashboardHost}, treating as direct access`); + + // Skip direct domain handling for API endpoints + if (req.path.startsWith('/api/')) { + console.log(`[HOST CHECK] Skipping direct domain handling for API endpoint: ${req.path}`); + return next(); + } + + // Extract domain from the hostname + const domain = normalizedHost; + console.log(`[DIRECT ACCESS] Using domain: ${domain} from host: ${host}`); + + try { + return await handleDirectDomainAccess(req, res, domain); + } catch (error) { + console.error(`[HOST CHECK] Error handling direct access, falling back to normal processing: ${error}`); + // Fall back to normal processing if direct access fails + return next(); + } + } + + console.log(`[HOST CHECK] Host ${normalizedHost} matches dashboard ${normalizedDashboardHost}, continuing with normal processing`); + // Continue with normal processing for dashboard host + next(); +}); + +// Serve static files AFTER checking for direct domain access app.use(express.static(path.join(__dirname, 'public'))); // Helper function to inject tracking script into HTML content @@ -128,77 +186,85 @@ function makeLinksAbsolute(content, mimeType, domain, subPath = '', isDirectAcce // Helper function to handle direct domain access async function handleDirectDomainAccess(req, res, domain) { + console.log(`[DIRECT ACCESS] Starting direct domain access handler for: ${domain}`); try { - const subPath = req.path || ''; + // Remove trailing slash from path for consistency + let subPath = req.path || ''; + if (subPath === '/' || subPath === '') { + subPath = ''; + } - console.log(`Direct access for domain: ${domain}, path: ${subPath}`); + console.log(`[DIRECT ACCESS] Domain: ${domain}, Path: '${subPath}'`); // Resolve Handshake domain to get IPFS CID + console.log(`[DIRECT ACCESS] Resolving Handshake domain: ${domain}`); const cid = await resolveHandshake(domain); if (!cid) { - console.warn(`No IPFS CID found for domain: ${domain}`); + console.warn(`[DIRECT ACCESS] No IPFS CID found for domain: ${domain}`); return res.status(404).json({ error: 'Domain not found or has no IPFS record', domain: domain }); } - console.log(`Resolved ${domain} to IPFS CID: ${cid}`); + console.log(`[DIRECT ACCESS] Resolved ${domain} to IPFS CID: ${cid}`); - // Fetch content from IPFS - const path = subPath.startsWith('/') ? subPath.substring(1) : subPath; - const content = await fetchFromIpfs(cid, path); + // Fetch content from IPFS - handle root path specially + const path = subPath === '' ? '' : (subPath.startsWith('/') ? subPath.substring(1) : subPath); + console.log(`[DIRECT ACCESS] Fetching IPFS content for CID: ${cid}, path: '${path}'`); + let content = await fetchFromIpfs(cid, path); if (!content) { - return res.status(404).json({ - error: 'Content not found on IPFS network', - cid: cid, - path: path - }); + console.log(`[DIRECT ACCESS] No content found for path: '${path}'`); + // Try index.html for empty paths + if (path === '') { + console.log('[DIRECT ACCESS] Trying index.html for root path'); + const indexContent = await fetchFromIpfs(cid, 'index.html'); + if (indexContent) { + console.log('[DIRECT ACCESS] Found index.html content, using that instead'); + content = indexContent; + } else { + console.log('[DIRECT ACCESS] No index.html found either'); + } + } + + // If still no content, return 404 + if (!content) { + console.log(`[DIRECT ACCESS] Returning 404 for CID: ${cid}, path: '${path}'`); + return res.status(404).json({ + error: 'Content not found on IPFS network', + cid: cid, + path: path + }); + } } // Set appropriate content type if (content.mimeType) { + console.log(`[DIRECT ACCESS] Setting content type: ${content.mimeType}`); res.setHeader('Content-Type', content.mimeType); } // Process HTML content: make links absolute and inject tracking script - // Note: isDirectAccess=true so links won't be prefixed with domain + console.log('[DIRECT ACCESS] Processing content'); let processedContent = content.data; if (content.mimeType && content.mimeType.includes('text/html')) { + console.log('[DIRECT ACCESS] HTML content detected, making links absolute'); processedContent = makeLinksAbsolute(processedContent, content.mimeType, domain, path, true); + console.log('[DIRECT ACCESS] Injecting tracking script'); processedContent = injectTrackingScript(processedContent, content.mimeType); } // Return the content + console.log('[DIRECT ACCESS] Sending processed content'); return res.send(processedContent); } catch (error) { - console.error('Error handling direct domain access:', error); - return res.status(500).json({ - error: 'Server error processing request', - message: error.message - }); + console.error('[DIRECT ACCESS] Error handling direct domain access:', error); + throw error; // Rethrow so middleware can fall back to normal processing } } -// Middleware to check if request is for direct domain access -app.use(async (req, res, next) => { - const host = req.get('host'); - - // If this is not the main dashboard host, treat it as direct domain access - if (host && host !== DASHBOARD_HOST) { - // Extract domain from the hostname - // This assumes the domain is the full hostname or a subdomain of your gateway - const domain = host.split(':')[0]; // Remove port if present - - return handleDirectDomainAccess(req, res, domain); - } - - // Continue with normal processing for dashboard host - next(); -}); - // Status endpoint app.get('/api/status', (req, res) => { res.json({ status: 'online', version: '1.0.0' }); @@ -208,13 +274,16 @@ app.get('/api/status', (req, res) => { app.get('/:domain', async (req, res, next) => { const domain = req.params.domain; + console.log(`[DASHBOARD ROUTE] Processing /:domain request for: ${domain}`); + // Skip this handler for reserved paths if (RESERVED_PATHS.includes(domain)) { + console.log(`[DASHBOARD ROUTE] Domain '${domain}' is in reserved paths, skipping`); return next(); } try { - console.log(`Processing request for domain root: ${domain}`); + console.log(`[DASHBOARD ROUTE] Processing request for domain root: ${domain}`); // Resolve Handshake domain to get IPFS CID const cid = await resolveHandshake(domain); @@ -472,6 +541,8 @@ app.get('*', (req, res) => { // Start server app.listen(PORT, () => { + console.log(`\n==================================`); console.log(`Fire Portal server running on port ${PORT}`); console.log(`Dashboard host: ${DASHBOARD_HOST}`); + console.log(`==================================\n`); });