Compare commits
5 Commits
V0.01-ifra
...
V2.0-beta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9faf7b5cef | ||
|
|
c984601352 | ||
|
|
8cdca7bb8c | ||
|
|
cbf2172fe4 | ||
|
|
a22040b705 |
22
README.md
22
README.md
@@ -1,12 +1,14 @@
|
||||
# Noderunners Relay
|
||||
|
||||
A high-performance Nostr relay built by Bitcoiners, for Bitcoiners. This project provides a web interface for managing access to the Noderunners relay service.
|
||||
```
|
||||
wss://relay.noderunners.network
|
||||
```
|
||||
|
||||

|
||||
|
||||
## Features
|
||||
|
||||
- 🚀 Lightning-fast relay performance with strfry v1.0.3
|
||||
- ⚡ Lightning Network integration for payments
|
||||
- 🔒 Secure authentication with Nostr
|
||||
- 💻 Modern, responsive web interface
|
||||
@@ -116,20 +118,6 @@ You can combine iframe mode with URL-based authentication:
|
||||
<iframe src="https://your-relay-domain.com?iframe=1&npub=npub1..." width="100%" height="600px"></iframe>
|
||||
```
|
||||
|
||||
## Supported NIPs
|
||||
|
||||
The relay supports the following Nostr Implementation Possibilities (NIPs):
|
||||
- NIP-01: Basic protocol flow description
|
||||
- NIP-02: Contact List and Petnames
|
||||
- NIP-04: Encrypted Direct Messages
|
||||
- NIP-09: Event Deletion
|
||||
- NIP-11: Relay Information Document
|
||||
- NIP-22: Event `created_at` Limits
|
||||
- NIP-28: Public Chat
|
||||
- NIP-40: Expiration Timestamp
|
||||
- NIP-70: Relay Payment Info
|
||||
- NIP-77: Lightning Network Relay Payment
|
||||
|
||||
## API Services
|
||||
|
||||
### LNbits Integration
|
||||
@@ -156,10 +144,6 @@ The relay supports the following Nostr Implementation Possibilities (NIPs):
|
||||
|
||||
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
||||
|
||||
## Support
|
||||
|
||||
For support, join our [Telegram group](https://t.me/noderunners) or visit [our website](https://noderunners.network).
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
- Built by the Noderunners community
|
||||
|
||||
47
src/App.tsx
47
src/App.tsx
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
|
||||
import React, { useEffect } from 'react';
|
||||
import { BrowserRouter as Router, Routes, Route, useSearchParams, useNavigate } from 'react-router-dom';
|
||||
import { Home } from './pages/Home';
|
||||
import { Login } from './pages/Login';
|
||||
import { Dashboard } from './pages/Dashboard';
|
||||
@@ -7,20 +7,43 @@ import { Payment } from './pages/Payment';
|
||||
import { ThankYou } from './pages/ThankYou';
|
||||
import { Terms } from './pages/Terms';
|
||||
import { Layout } from './components/Layout';
|
||||
import { useStore } from './store/useStore';
|
||||
|
||||
// Wrapper component to handle auto-login logic
|
||||
function AutoLoginHandler({ children }: { children: React.ReactNode }) {
|
||||
const [searchParams] = useSearchParams();
|
||||
const navigate = useNavigate();
|
||||
const { user, setUser } = useStore();
|
||||
|
||||
useEffect(() => {
|
||||
const urlPubkey = searchParams.get('npub') || searchParams.get('pubkey');
|
||||
const isIframe = searchParams.get('iframe') === '1';
|
||||
|
||||
// Auto-login if pubkey/npub is in URL and user isn't already logged in
|
||||
if (urlPubkey && !user) {
|
||||
setUser({ pubkey: urlPubkey, isWhitelisted: false });
|
||||
navigate(isIframe ? '/dashboard?iframe=1' : '/dashboard');
|
||||
}
|
||||
}, [searchParams, user, setUser, navigate]);
|
||||
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<Router>
|
||||
<Routes>
|
||||
<Route path="/" element={<Layout />}>
|
||||
<Route index element={<Home />} />
|
||||
<Route path="login" element={<Login />} />
|
||||
<Route path="dashboard" element={<Dashboard />} />
|
||||
<Route path="payment" element={<Payment />} />
|
||||
<Route path="thank-you" element={<ThankYou />} />
|
||||
<Route path="terms" element={<Terms />} />
|
||||
</Route>
|
||||
</Routes>
|
||||
<AutoLoginHandler>
|
||||
<Routes>
|
||||
<Route path="/" element={<Layout />}>
|
||||
<Route index element={<Home />} />
|
||||
<Route path="login" element={<Login />} />
|
||||
<Route path="dashboard" element={<Dashboard />} />
|
||||
<Route path="payment" element={<Payment />} />
|
||||
<Route path="thank-you" element={<ThankYou />} />
|
||||
<Route path="terms" element={<Terms />} />
|
||||
</Route>
|
||||
</Routes>
|
||||
</AutoLoginHandler>
|
||||
</Router>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,12 +10,21 @@ export function Login() {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [pubkeyInput, setPubkeyInput] = useState('');
|
||||
const isIframe = searchParams.get('iframe') === '1';
|
||||
const urlPubkey = searchParams.get('npub') || searchParams.get('pubkey');
|
||||
|
||||
useEffect(() => {
|
||||
// Handle URL-based login
|
||||
if (urlPubkey && !user) {
|
||||
setUser({ pubkey: urlPubkey, isWhitelisted: false });
|
||||
navigate(isIframe ? '/dashboard?iframe=1' : '/dashboard');
|
||||
return;
|
||||
}
|
||||
|
||||
// Regular user redirect
|
||||
if (user) {
|
||||
navigate(isIframe ? '/dashboard?iframe=1' : '/dashboard');
|
||||
}
|
||||
}, [user, navigate, isIframe]);
|
||||
}, [user, navigate, isIframe, urlPubkey, setUser]);
|
||||
|
||||
const handleExtensionLogin = async () => {
|
||||
setIsLoading(true);
|
||||
@@ -73,6 +82,15 @@ export function Login() {
|
||||
}
|
||||
};
|
||||
|
||||
// If we're processing URL-based login, show loading state
|
||||
if (urlPubkey && !user) {
|
||||
return (
|
||||
<div className="flex justify-center items-center min-h-[400px]">
|
||||
<div className="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-orange-500"></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="max-w-md mx-auto bg-gray-800 rounded-lg p-8">
|
||||
<h1 className="text-2xl font-bold mb-6 text-center">Connect with Nostr</h1>
|
||||
|
||||
Reference in New Issue
Block a user