diff --git a/README.md b/README.md index 534032e..3e86475 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,167 @@ -# Noderunners-relay-front -The noderunners Nostr relay front-end +# 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. + +![Noderunners Relay](https://cdn.azzamo.net/5cc03420a18166ef7a20b1e6b7dad240ad7d634824649643c80d74a924062258.png) + +## Features + +- 🚀 Lightning-fast relay performance with strfry v1.0.3 +- ⚡ Lightning Network integration for payments +- 🔒 Secure authentication with Nostr +- 💻 Modern, responsive web interface +- 📊 Real-time relay statistics +- 🔍 Uptime monitoring +- 🖼️ Iframe support for embedding +- 🔑 Multiple login methods (Extension, Manual, URL-based) + +## Tech Stack + +- **Frontend**: React + TypeScript + Vite +- **Styling**: Tailwind CSS +- **Icons**: Lucide React +- **Payment**: LNbits Integration +- **Authentication**: Nostr Protocol +- **State Management**: Zustand + +## Environment Variables + +Create a `.env` file in the root directory with the following variables: + +```env +# LNbits Configuration +VITE_LNBITS_URL="your-lnbits-url" +VITE_LNBITS_API_KEY="your-api-key" + +# App Settings +VITE_APP_NAME="Noderunners Relay" +VITE_APP_DESCRIPTION="A high-performance Nostr relay built by Bitcoiners, for Bitcoiners" +VITE_LOGO_URL="your-logo-url" +VITE_GITHUB_URL="your-github-url" + +# Nostr Settings +VITE_NOSTR_RELAY_URL="wss://your-relay-url" +VITE_API_URL="your-api-url" +VITE_SUPPORTED_NIPS="1,2,4,9,11,22,28,40,70,77" +VITE_RELAY_SOFTWARE="strfry v1.0.3" + +# Payment Settings +VITE_MIN_PAYMENT_AMOUNT=10000 +VITE_PAYMENT_MEMO="Noderunners Relay Access" +VITE_PAYMENT_CURRENCY="sat" +VITE_WEBHOOK_URL="your-webhook-url" + +# Feature Flags +VITE_ENABLE_WHITELIST=true +VITE_ENABLE_PAYMENT_VERIFICATION=true +VITE_ENABLE_DEMO=false + +# Uptime Monitoring +VITE_UPTIME_KUMA_URL="your-uptime-kuma-url" +VITE_UPTIME_KUMA_ID="1" +``` + +## Development + +1. Install dependencies: +```bash +npm install +``` + +2. Start the development server: +```bash +npm run dev +``` + +3. Build for production: +```bash +npm run build +``` + +## Authentication Methods + +The application supports multiple authentication methods: + +1. **Nostr Extension** + - Uses browser extensions like Alby for seamless authentication + - Automatically retrieves the user's public key + +2. **Manual Entry** + - Users can manually input their npub or hex public key + - Supports both formats for maximum flexibility + +3. **URL-based Authentication** + - Automatically logs in using URL parameters + - Supports both `npub` and `pubkey` parameters + - Example URLs: + ``` + https://your-domain.com?npub=npub1... + https://your-domain.com?pubkey=abc123... + ``` + +## Iframe Integration + +The application supports iframe embedding with a clean interface. Add `?iframe=1` to the URL to: +- Hide header and footer +- Show a logout button on the dashboard +- Maintain a minimal interface + +Example: +```html + +``` + +You can combine iframe mode with URL-based authentication: +```html + +``` + +## 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 +- Invoice creation +- Payment verification +- Exchange rate conversion +- Wallet information + +### Relay API +- User information +- Whitelist management +- Payment processing +- Status monitoring + +## Contributing + +1. Fork the repository +2. Create your feature branch (`git checkout -b feature/amazing-feature`) +3. Commit your changes (`git commit -m 'Add some amazing feature'`) +4. Push to the branch (`git push origin feature/amazing-feature`) +5. Open a Pull Request + +## License + +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 +- Powered by [strfry](https://github.com/hoytech/strfry) +- Lightning Network integration via [LNbits](https://lnbits.com) \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx index a9d03b2..2aaca2c 100644 --- a/src/App.tsx +++ b/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 ( - - }> - } /> - } /> - } /> - } /> - } /> - } /> - - + + + }> + } /> + } /> + } /> + } /> + } /> + } /> + + + ); } diff --git a/src/components/Layout.tsx b/src/components/Layout.tsx index 0cf129f..2cbe053 100644 --- a/src/components/Layout.tsx +++ b/src/components/Layout.tsx @@ -1,8 +1,21 @@ import React from 'react'; -import { Link, Outlet } from 'react-router-dom'; +import { Link, Outlet, useSearchParams } from 'react-router-dom'; import { Navigation } from './Navigation'; export function Layout() { + const [searchParams] = useSearchParams(); + const isIframe = searchParams.get('iframe') === '1'; + + if (isIframe) { + return ( +
+
+ +
+
+ ); + } + return (
diff --git a/src/components/Navigation.tsx b/src/components/Navigation.tsx index 44d369f..10e7f90 100644 --- a/src/components/Navigation.tsx +++ b/src/components/Navigation.tsx @@ -1,18 +1,21 @@ import React, { useState } from 'react'; -import { Link, useLocation, useNavigate } from 'react-router-dom'; +import { Link, useLocation, useNavigate, useSearchParams } from 'react-router-dom'; import { Flame, Menu, X } from 'lucide-react'; import { useStore } from '../store/useStore'; export function Navigation() { const location = useLocation(); const navigate = useNavigate(); + const [searchParams] = useSearchParams(); const { user, setUser } = useStore(); const [isMenuOpen, setIsMenuOpen] = useState(false); + const isIframe = searchParams.get('iframe') === '1'; const handleLogout = () => { setUser(null); setIsMenuOpen(false); - navigate('/login', { replace: true }); // Use replace to prevent going back to dashboard + const newPath = '/login' + (isIframe ? '?iframe=1' : ''); + navigate(newPath, { replace: true }); }; const handleMenuClick = () => { @@ -23,11 +26,16 @@ export function Navigation() { setIsMenuOpen(false); }; + // Helper function to add iframe parameter to paths + const getPath = (path: string) => { + return isIframe ? `${path}?iframe=1` : path; + }; + return (