Compare commits
6 Commits
c2f93a87e3
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
2ba2ef3bef
|
|||
|
a7d18d529e
|
|||
| 901b02fbdc | |||
|
c78d5f5d63
|
|||
|
3d40a7152f
|
|||
|
02cf96f3eb
|
@@ -1,5 +1,6 @@
|
|||||||
# Server settings
|
# Server settings
|
||||||
PORT=3000
|
PORT=3000
|
||||||
|
DASHBOARD_HOST=ipfs.hnsproxy.au
|
||||||
|
|
||||||
# IPFS settings
|
# IPFS settings
|
||||||
IPFS_GATEWAY=https://ipfs.io
|
IPFS_GATEWAY=https://ipfs.io
|
||||||
|
|||||||
@@ -84,12 +84,13 @@ To proxy Handshake domains to IPFS, you can use the following nginx configuratio
|
|||||||
```nginx
|
```nginx
|
||||||
server {
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
server_name ~^(?<domain>.+)$;
|
server_name _; # Catch-all for all domains
|
||||||
|
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
proxy_pass http://127.0.0.1:3000/$domain;
|
proxy_pass http://127.0.0.1:3000;
|
||||||
|
|
||||||
|
proxy_set_header Host $host;
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ const {
|
|||||||
LOCAL_RESOLVER_HOST,
|
LOCAL_RESOLVER_HOST,
|
||||||
LOCAL_RESOLVER_PORT,
|
LOCAL_RESOLVER_PORT,
|
||||||
CACHE_ENABLED,
|
CACHE_ENABLED,
|
||||||
CACHE_TTL_SECONDS
|
CACHE_TTL_SECONDS,
|
||||||
|
IPFS_GATEWAY
|
||||||
} = config;
|
} = config;
|
||||||
|
|
||||||
// Setup cache
|
// Setup cache
|
||||||
@@ -18,6 +19,12 @@ const cache = new NodeCache({
|
|||||||
checkperiod: CACHE_TTL_SECONDS * 0.2,
|
checkperiod: CACHE_TTL_SECONDS * 0.2,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Setup separate cache for IPNS with shorter TTL (IPNS can change more frequently)
|
||||||
|
const ipnsCache = new NodeCache({
|
||||||
|
stdTTL: Math.min(CACHE_TTL_SECONDS, 300), // Max 5 minutes for IPNS
|
||||||
|
checkperiod: 60,
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve a Handshake domain to an IPFS CID
|
* Resolve a Handshake domain to an IPFS CID
|
||||||
* @param {string} domain - The Handshake domain to resolve
|
* @param {string} domain - The Handshake domain to resolve
|
||||||
@@ -299,14 +306,14 @@ async function resolveLocal(domain) {
|
|||||||
/**
|
/**
|
||||||
* Extract IPFS CID from DNS TXT records
|
* Extract IPFS CID from DNS TXT records
|
||||||
* @param {string[][]} records - Array of TXT record arrays
|
* @param {string[][]} records - Array of TXT record arrays
|
||||||
* @returns {string|null} - IPFS CID or null
|
* @returns {Promise<string|null>} - IPFS CID or IPNS hash or null
|
||||||
*/
|
*/
|
||||||
function extractCidFromRecords(records) {
|
async function extractCidFromRecords(records) {
|
||||||
if (!records || !records.length) {
|
if (!records || !records.length) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flatten and look for ipfs= or ip6= prefixes
|
// Flatten and look for ipfs=, ip6=, or ipns= prefixes
|
||||||
for (const recordSet of records) {
|
for (const recordSet of records) {
|
||||||
for (const record of recordSet) {
|
for (const record of recordSet) {
|
||||||
// Support multiple formats
|
// Support multiple formats
|
||||||
@@ -319,6 +326,18 @@ function extractCidFromRecords(records) {
|
|||||||
if (record.startsWith('ip6=')) {
|
if (record.startsWith('ip6=')) {
|
||||||
return record.substring(4);
|
return record.substring(4);
|
||||||
}
|
}
|
||||||
|
if (record.startsWith('ipns=')) {
|
||||||
|
const ipnsName = record.substring(5);
|
||||||
|
console.log(`Found IPNS record: ${ipnsName}`);
|
||||||
|
// Return IPNS hash prefixed with 'ipns:' to distinguish from IPFS CID
|
||||||
|
return `ipns:${ipnsName}`;
|
||||||
|
}
|
||||||
|
if (record.startsWith('ipns:')) {
|
||||||
|
const ipnsName = record.substring(5);
|
||||||
|
console.log(`Found IPNS record: ${ipnsName}`);
|
||||||
|
// Return IPNS hash prefixed with 'ipns:' to distinguish from IPFS CID
|
||||||
|
return `ipns:${ipnsName}`;
|
||||||
|
}
|
||||||
|
|
||||||
// Log the record for debugging
|
// Log the record for debugging
|
||||||
console.log(`Found TXT record: ${record}`);
|
console.log(`Found TXT record: ${record}`);
|
||||||
@@ -328,7 +347,6 @@ function extractCidFromRecords(records) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear the cache for a specific domain
|
* Clear the cache for a specific domain
|
||||||
* @param {string} domain - The Handshake domain to clear from cache
|
* @param {string} domain - The Handshake domain to clear from cache
|
||||||
@@ -340,7 +358,46 @@ function clearCache(domain) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear IPNS cache for a specific IPNS name (keeping for API compatibility)
|
||||||
|
* @param {string} ipnsName - The IPNS name to clear from cache
|
||||||
|
*/
|
||||||
|
function clearIpnsCache(ipnsName) {
|
||||||
|
// Since we're not caching IPNS resolutions anymore, just log
|
||||||
|
console.log(`IPNS cache clear requested for ${ipnsName} (no-op)`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the cache for a specific domain
|
||||||
|
* @param {string} domain - The Handshake domain to clear from cache
|
||||||
|
*/
|
||||||
|
function clearCache(domain) {
|
||||||
|
if (CACHE_ENABLED) {
|
||||||
|
cache.del(`hns:${domain}`);
|
||||||
|
// Also clear any IPNS cache entries that might be related
|
||||||
|
const keys = ipnsCache.keys();
|
||||||
|
keys.forEach(key => {
|
||||||
|
if (key.includes(domain)) {
|
||||||
|
ipnsCache.del(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
console.log(`Cache cleared for ${domain}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear IPNS cache for a specific IPNS name
|
||||||
|
* @param {string} ipnsName - The IPNS name to clear from cache
|
||||||
|
*/
|
||||||
|
function clearIpnsCache(ipnsName) {
|
||||||
|
if (CACHE_ENABLED) {
|
||||||
|
ipnsCache.del(`ipns:${ipnsName}`);
|
||||||
|
console.log(`IPNS cache cleared for ${ipnsName}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
resolveHandshake,
|
resolveHandshake,
|
||||||
clearCache
|
clearCache,
|
||||||
|
clearIpnsCache
|
||||||
};
|
};
|
||||||
|
|||||||
38
lib/ipfs.js
38
lib/ipfs.js
@@ -24,26 +24,30 @@ const mimeTypes = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch content from IPFS by CID and path
|
* Fetch content from IPFS by CID and path, or from IPNS
|
||||||
* @param {string} cid - IPFS Content Identifier
|
* @param {string} cidOrIpns - IPFS Content Identifier or IPNS hash (prefixed with 'ipns:')
|
||||||
* @param {string} path - Optional path within the CID
|
* @param {string} path - Optional path within the CID/IPNS
|
||||||
* @returns {Promise<{data: Buffer, mimeType: string}|null>} - Content and MIME type or null
|
* @returns {Promise<{data: Buffer, mimeType: string}|null>} - Content and MIME type or null
|
||||||
*/
|
*/
|
||||||
async function fetchFromIpfs(cid, path = '') {
|
async function fetchFromIpfs(cidOrIpns, path = '') {
|
||||||
const contentPath = path ? `${cid}/${path}` : cid;
|
const isIpns = cidOrIpns.startsWith('ipns:');
|
||||||
|
const hash = isIpns ? cidOrIpns.substring(5) : cidOrIpns;
|
||||||
|
const contentType = isIpns ? 'ipns' : 'ipfs';
|
||||||
|
const contentPath = path ? `${hash}/${path}` : hash;
|
||||||
|
|
||||||
// Check cache first
|
// Check cache first
|
||||||
if (CACHE_ENABLED) {
|
if (CACHE_ENABLED) {
|
||||||
const cachedContent = cache.get(`ipfs:${contentPath}`);
|
const cacheKey = `${contentType}:${contentPath}`;
|
||||||
|
const cachedContent = cache.get(cacheKey);
|
||||||
if (cachedContent) {
|
if (cachedContent) {
|
||||||
console.log(`Cache hit for IPFS content: ${contentPath}`);
|
console.log(`Cache hit for ${contentType.toUpperCase()} content: ${contentPath}`);
|
||||||
return cachedContent;
|
return cachedContent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Use the HTTP gateway directly instead of the IPFS client
|
// Use the HTTP gateway directly
|
||||||
const result = await fetchViaGateway(cid, path);
|
const result = await fetchViaGateway(hash, path, contentType);
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
return null;
|
return null;
|
||||||
@@ -56,25 +60,29 @@ async function fetchFromIpfs(cid, path = '') {
|
|||||||
|
|
||||||
// Cache the result
|
// Cache the result
|
||||||
if (CACHE_ENABLED) {
|
if (CACHE_ENABLED) {
|
||||||
cache.set(`ipfs:${contentPath}`, result);
|
const cacheKey = `${contentType}:${contentPath}`;
|
||||||
|
// Use shorter TTL for IPNS content since it can change
|
||||||
|
const ttl = isIpns ? Math.min(CACHE_TTL_SECONDS, 300) : CACHE_TTL_SECONDS;
|
||||||
|
cache.set(cacheKey, result, ttl);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Error fetching ${contentPath} from IPFS:`, error);
|
console.error(`Error fetching ${contentPath} from ${contentType.toUpperCase()}:`, error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch content via IPFS HTTP gateway
|
* Fetch content via IPFS HTTP gateway
|
||||||
* @param {string} cid - IPFS Content Identifier
|
* @param {string} hash - IPFS CID or IPNS hash
|
||||||
* @param {string} path - Path within the CID
|
* @param {string} path - Path within the content
|
||||||
|
* @param {string} contentType - Either 'ipfs' or 'ipns'
|
||||||
* @returns {Promise<{data: Buffer, mimeType: string}|null>} - Content and MIME type or null
|
* @returns {Promise<{data: Buffer, mimeType: string}|null>} - Content and MIME type or null
|
||||||
*/
|
*/
|
||||||
async function fetchViaGateway(cid, path) {
|
async function fetchViaGateway(hash, path, contentType = 'ipfs') {
|
||||||
try {
|
try {
|
||||||
const url = new URL(`${IPFS_GATEWAY}/ipfs/${cid}${path ? '/' + path : ''}`);
|
const url = new URL(`${IPFS_GATEWAY}/${contentType}/${hash}${path ? '/' + path : ''}`);
|
||||||
console.log(`Fetching from IPFS gateway: ${url}`);
|
console.log(`Fetching from IPFS gateway: ${url}`);
|
||||||
|
|
||||||
const response = await fetch(url);
|
const response = await fetch(url);
|
||||||
|
|||||||
@@ -157,13 +157,17 @@
|
|||||||
<ol>
|
<ol>
|
||||||
<li>Get the IPFS hash of your content using any IPFS content hosting provider</li>
|
<li>Get the IPFS hash of your content using any IPFS content hosting provider</li>
|
||||||
<li>Update your Handshake domain's DNS records with a TXT record</li>
|
<li>Update your Handshake domain's DNS records with a TXT record</li>
|
||||||
<li>Use the format: <code>ipfs=Qm...your-ipfs-hash</code></li>
|
<li>Use the format: <code>ipfs=Qm...your-ipfs-hash</code> or using IPNS <code>ipns=your-ipns-hash</code></li>
|
||||||
<li>Add an A record to point your domain to this portal <code>192.9.167.104</code> <br>
|
<li>Add an A record to point your domain to this portal <code>192.9.167.104</code> <br>
|
||||||
Alternatively you can use this ALIAS <code>ipfs.hnshosting</code></li>
|
Alternatively you can use this ALIAS <code>ipfs.hnshosting</code></li>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li>Wait a few minutes and you should be able to see your IPFS content on the domain</li>
|
<li>Wait a few minutes and you should be able to see your IPFS content on the domain</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
|
<a href="https://www.youtube.com/watch?v=78oa_a1zlLk" target="_blank" class="video-tutorial">
|
||||||
|
<strong>Watch a video tutorial</strong>
|
||||||
|
</a>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="info-section">
|
<section class="info-section">
|
||||||
|
|||||||
193
server.js
193
server.js
@@ -3,7 +3,7 @@ const express = require('express');
|
|||||||
const morgan = require('morgan');
|
const morgan = require('morgan');
|
||||||
const cors = require('cors');
|
const cors = require('cors');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const { resolveHandshake, clearCache } = require('./lib/handshake');
|
const { resolveHandshake, clearCache, clearIpnsCache } = require('./lib/handshake');
|
||||||
const { fetchFromIpfs } = require('./lib/ipfs');
|
const { fetchFromIpfs } = require('./lib/ipfs');
|
||||||
const { PORT } = require('./config');
|
const { PORT } = require('./config');
|
||||||
|
|
||||||
@@ -14,6 +14,22 @@ app.use(cors());
|
|||||||
app.use(morgan('dev'));
|
app.use(morgan('dev'));
|
||||||
app.use(express.json());
|
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';
|
||||||
|
|
||||||
// Define reserved paths that should not be treated as Handshake domains
|
// Define reserved paths that should not be treated as Handshake domains
|
||||||
const RESERVED_PATHS = [
|
const RESERVED_PATHS = [
|
||||||
'api',
|
'api',
|
||||||
@@ -27,7 +43,53 @@ const RESERVED_PATHS = [
|
|||||||
'favicon.ico'
|
'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')));
|
app.use(express.static(path.join(__dirname, 'public')));
|
||||||
|
|
||||||
// Helper function to inject tracking script into HTML content
|
// Helper function to inject tracking script into HTML content
|
||||||
@@ -54,13 +116,14 @@ function injectTrackingScript(content, mimeType) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to make links absolute in HTML content
|
// Helper function to make links absolute in HTML content
|
||||||
function makeLinksAbsolute(content, mimeType, domain, subPath = '') {
|
function makeLinksAbsolute(content, mimeType, domain, subPath = '', isDirectAccess = false) {
|
||||||
if (!mimeType || !mimeType.includes('text/html')) {
|
if (!mimeType || !mimeType.includes('text/html')) {
|
||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
let htmlContent = content.toString();
|
let htmlContent = content.toString();
|
||||||
const baseUrl = `/${domain}`;
|
// If direct access (via subdomain), don't prefix links with domain
|
||||||
|
const baseUrl = isDirectAccess ? '' : `/${domain}`;
|
||||||
|
|
||||||
// Create base directory for proper path resolution
|
// Create base directory for proper path resolution
|
||||||
let basePath = '/';
|
let basePath = '/';
|
||||||
@@ -121,6 +184,87 @@ function makeLinksAbsolute(content, mimeType, domain, subPath = '') {
|
|||||||
return htmlContent;
|
return htmlContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
// Remove trailing slash from path for consistency
|
||||||
|
let subPath = req.path || '';
|
||||||
|
if (subPath === '/' || subPath === '') {
|
||||||
|
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(`[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(`[DIRECT ACCESS] Resolved ${domain} to IPFS CID: ${cid}`);
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
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
|
||||||
|
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('[DIRECT ACCESS] Error handling direct domain access:', error);
|
||||||
|
throw error; // Rethrow so middleware can fall back to normal processing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Status endpoint
|
// Status endpoint
|
||||||
app.get('/api/status', (req, res) => {
|
app.get('/api/status', (req, res) => {
|
||||||
res.json({ status: 'online', version: '1.0.0' });
|
res.json({ status: 'online', version: '1.0.0' });
|
||||||
@@ -130,13 +274,16 @@ app.get('/api/status', (req, res) => {
|
|||||||
app.get('/:domain', async (req, res, next) => {
|
app.get('/:domain', async (req, res, next) => {
|
||||||
const domain = req.params.domain;
|
const domain = req.params.domain;
|
||||||
|
|
||||||
|
console.log(`[DASHBOARD ROUTE] Processing /:domain request for: ${domain}`);
|
||||||
|
|
||||||
// Skip this handler for reserved paths
|
// Skip this handler for reserved paths
|
||||||
if (RESERVED_PATHS.includes(domain)) {
|
if (RESERVED_PATHS.includes(domain)) {
|
||||||
|
console.log(`[DASHBOARD ROUTE] Domain '${domain}' is in reserved paths, skipping`);
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
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
|
// Resolve Handshake domain to get IPFS CID
|
||||||
const cid = await resolveHandshake(domain);
|
const cid = await resolveHandshake(domain);
|
||||||
@@ -387,6 +534,39 @@ app.get('/api/refresh/:domain', async (req, res) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// New API route to refresh IPNS cache specifically
|
||||||
|
app.get('/api/refresh-ipns/:ipnsName', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const ipnsName = req.params.ipnsName;
|
||||||
|
|
||||||
|
// Validate IPNS name format (basic validation)
|
||||||
|
if (!ipnsName.match(/^[a-zA-Z0-9]+$/)) {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Invalid IPNS name format'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Refreshing IPNS cache for: ${ipnsName}`);
|
||||||
|
// Clear IPNS cache
|
||||||
|
clearIpnsCache(ipnsName);
|
||||||
|
|
||||||
|
// Return success response
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
message: `IPNS cache refresh initiated for ${ipnsName}`,
|
||||||
|
timestamp: new Date().toISOString()
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('IPNS refresh error:', error);
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Server error during IPNS refresh operation'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Catch-all route to handle SPA navigation
|
// Catch-all route to handle SPA navigation
|
||||||
app.get('*', (req, res) => {
|
app.get('*', (req, res) => {
|
||||||
res.sendFile(path.join(__dirname, 'public', 'index.html'));
|
res.sendFile(path.join(__dirname, 'public', 'index.html'));
|
||||||
@@ -394,5 +574,8 @@ app.get('*', (req, res) => {
|
|||||||
|
|
||||||
// Start server
|
// Start server
|
||||||
app.listen(PORT, () => {
|
app.listen(PORT, () => {
|
||||||
|
console.log(`\n==================================`);
|
||||||
console.log(`Fire Portal server running on port ${PORT}`);
|
console.log(`Fire Portal server running on port ${PORT}`);
|
||||||
|
console.log(`Dashboard host: ${DASHBOARD_HOST}`);
|
||||||
|
console.log(`==================================\n`);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user