feat: Initial code drop

This commit is contained in:
2025-06-17 18:28:35 +10:00
commit 0e4c5e12a0
13 changed files with 7541 additions and 0 deletions

67
public/app.js Normal file
View File

@@ -0,0 +1,67 @@
document.addEventListener('DOMContentLoaded', () => {
const domainInput = document.getElementById('domainInput');
const searchBtn = document.getElementById('searchBtn');
const exampleLinks = document.querySelectorAll('.example-link');
const statusIndicator = document.getElementById('status-indicator');
// Check server status
checkServerStatus();
// Search button click handler
searchBtn.addEventListener('click', () => {
navigateToHnsDomain();
});
// Enter key press handler
domainInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
navigateToHnsDomain();
}
});
// Example link click handlers
exampleLinks.forEach(link => {
link.addEventListener('click', (e) => {
e.preventDefault();
const domain = e.target.getAttribute('data-domain');
domainInput.value = domain;
navigateToHnsDomain();
});
});
// Function to navigate to HNS domain
function navigateToHnsDomain() {
const domain = domainInput.value.trim();
if (!domain) {
alert('Please enter a Handshake domain');
return;
}
// Clean up domain input (remove trailing slashes)
const cleanDomain = domain.replace(/\/+$/, '');
// Navigate to the HNS domain
window.location.href = `/hns/${cleanDomain}`;
}
// Check server status
async function checkServerStatus() {
try {
const response = await fetch('/api/status');
const data = await response.json();
if (data.status === 'online') {
statusIndicator.textContent = 'Online';
statusIndicator.classList.add('online');
} else {
statusIndicator.textContent = 'Degraded';
statusIndicator.classList.add('offline');
}
} catch (error) {
statusIndicator.textContent = 'Offline';
statusIndicator.classList.add('offline');
console.error('Error checking server status:', error);
}
}
});

17
public/demo.html Normal file
View File

@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>IPFS.act</title>
</head>
<body style="background-color: black; color: blueviolet;text-align: center;">
<h1>IPFS.act</h1>
<p>This is a demo page running on IPFS.</p>
<p>It is designed to showcase the capabilities of IPFS and Handshake domains.</p>
<div style="position: absolute; bottom: 25px; left: 0; width: 100%; text-align: center;">
<a href="https://nathan.woodburn.au" style="color: white;">&copy; Nathan.Woodburn/</a>
</div>
</body>
</html>

74
public/index.html Normal file
View File

@@ -0,0 +1,74 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Fire Portal</title>
<link rel="stylesheet" href="styles.css">
<link rel="icon" href="https://woodburn.au/favicon.png" type="image/png">
</head>
<body>
<div class="container">
<header>
<h1>Fire Portal</h1>
<p>IPFS Gateway for Handshake Domains</p>
</header>
<main>
<section class="search-section">
<h2>Access Handshake + IPFS Content</h2>
<div class="search-form">
<input type="text" id="domainInput" placeholder="Enter a Handshake domain (e.g., example/)">
<button id="searchBtn">Go</button>
</div>
<div class="example-note">
<p>Examples:</p>
<ul>
<li><a href="#" class="example-link" data-domain="ipfs.act/">ipfs.act/</a> - Example Page created by Nathan.Woodburn/</li>
</ul>
</div>
</section>
<section class="info-section">
<h2>About Fire Portal</h2>
<p>
Fire Portal connects Handshake domains to IPFS content, enabling truly decentralized websites.
It resolves Handshake domains, finds their IPFS content identifiers, and serves the content.
</p>
<h3>How it works</h3>
<ol>
<li>A Handshake domain is resolved to get its DNS records</li>
<li>IPFS content identifiers are extracted from TXT records</li>
<li>Content is fetched from the IPFS network</li>
<li>The content is served to your browser</li>
</ol>
<p>
<strong>URL format:</strong> <code>https://ipfs.woodburn.au/hns/[domain]/[path]</code><br>
Replace <code>[domain]</code> with any Handshake domain and <code>[path]</code> with an optional path.
</p>
</section>
<section class="info-section">
<h2>How to setup IPFS on your domain</h2>
<p>
To set up your Handshake domain to work with IPFS, follow these steps:
</p>
<ol>
<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>Use the format: <code>ipfs=Qm...your-ipfs-hash</code></li>
<li>Wait a few minutes and you should be able to see your IPFS content on the domain</li>
</section>
</main>
<footer>
<p>&copy; 2025 <a href="https://nathan.woodburn.au">Nathan.Woodburn/</a> | <a href="https://git.woodburn.au/nathanwoodburn/fireportal">Git Repo</a></p>
<p><small>Status: <span id="status-indicator">Checking...</span></small></p>
</footer>
</div>
<script src="app.js"></script>
</body>
</html>

163
public/styles.css Normal file
View File

@@ -0,0 +1,163 @@
:root {
--primary-color: #ff5722;
--secondary-color: #ffab91;
--dark-color: #212121;
--light-color: #f5f5f5;
--accent-color: #ff9800;
--error-color: #f44336;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
line-height: 1.6;
color: var(--dark-color);
background-color: var(--light-color);
}
.container {
max-width: 900px;
margin: 0 auto;
padding: 2rem 1rem;
}
header {
text-align: center;
margin-bottom: 2rem;
}
header h1 {
color: var(--primary-color);
margin-bottom: 0.5rem;
}
section {
background-color: white;
border-radius: 8px;
padding: 2rem;
margin-bottom: 2rem;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
h2 {
color: var(--dark-color);
margin-bottom: 1rem;
}
h3 {
color: var(--dark-color);
margin: 1.5rem 0 0.5rem;
}
.search-form {
display: flex;
margin: 1.5rem 0;
}
input[type="text"] {
flex: 1;
padding: 0.8rem;
font-size: 1rem;
border: 2px solid #ddd;
border-radius: 4px 0 0 4px;
outline: none;
}
input[type="text"]:focus {
border-color: var(--primary-color);
}
button {
background-color: var(--primary-color);
color: white;
border: none;
padding: 0.8rem 1.5rem;
font-size: 1rem;
cursor: pointer;
border-radius: 0 4px 4px 0;
transition: background-color 0.2s;
}
button:hover {
background-color: var(--accent-color);
}
.example-note {
font-size: 0.9rem;
color: #666;
margin-top: 1rem;
}
.example-note ul {
list-style-type: none;
margin-top: 0.5rem;
}
.example-link {
color: var(--primary-color);
text-decoration: none;
}
.example-link:hover {
text-decoration: underline;
}
ol, ul {
margin-left: 1.5rem;
}
code {
background-color: #f0f0f0;
padding: 0.2rem 0.4rem;
border-radius: 3px;
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
}
footer {
text-align: center;
margin-top: 2rem;
color: #666;
font-size: 0.9rem;
}
footer a {
color: var(--primary-color);
text-decoration: none;
}
#status-indicator {
font-weight: bold;
}
.online {
color: #2e7d32;
}
.offline {
color: var(--error-color);
}
@media (max-width: 600px) {
section {
padding: 1.5rem;
}
.search-form {
flex-direction: column;
}
input[type="text"] {
border-radius: 4px;
margin-bottom: 0.5rem;
}
button {
width: 100%;
border-radius: 4px;
}
}