10 Commits

Author SHA1 Message Date
Michilis
da1d1b0d53 Merge pull request #4 from Michilis/dev
Move terms.txt to public/ to fix Terms page loading
2025-11-10 21:22:50 -03:00
Noderunners
0ab66479d9 Move terms.txt to public/ to fix Terms page loading 2025-11-11 01:21:52 +01:00
Michilis
1129fb9341 Merge pull request #3 from Michilis/dev
fix(lnbits): normalize invoice response (fallback to bolt11) and upda…
2025-11-10 16:51:25 -03:00
Noderunners
4afe3f6d3e fix(lnbits): normalize invoice response (fallback to bolt11) and update payment status check; chore: add .gitignore 2025-11-10 20:47:58 +01:00
Michilis
21bbd0fab1 Merge pull request #2 from Michilis/dev
Fix API calls to use npub instead of identifier
2025-11-06 17:17:48 -03:00
Michilis
bb3b5604a1 Fix API calls to use npub instead of identifier
- Updated getUserInfo to send npub field instead of identifier
- Updated whitelistUser to send npub field instead of identifier
- Resolves 422 Unprocessable Content error from backend API
2025-07-01 19:18:29 +00:00
Michilis
9faf7b5cef Update README.md 2025-02-10 05:04:59 +01:00
Michilis
c984601352 Update README.md 2025-02-10 05:04:42 +01:00
Michilis
8cdca7bb8c Update README.md 2025-02-10 04:50:11 +01:00
Michilis
cbf2172fe4 Merge pull request #1 from Michilis/V0.02-ref-login
V0.02 ref login
2025-02-10 04:47:27 +01:00
5 changed files with 113 additions and 43 deletions

64
.gitignore vendored Normal file
View File

@@ -0,0 +1,64 @@
# Dependencies
node_modules/
# Production builds
dist/
dist-ssr/
build/
# Vite and tooling caches
.vite/
.esbuild/
.rollup.cache/
.parcel-cache/
.turbo/
# Logs
logs/
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
# Environment files
.env
.env.local
.env.*.local
.env.production.local
.env.development.local
.env.test.local
!.env.example
# Coverage
coverage/
# TypeScript build info
*.tsbuildinfo
# Editor directories and files
.vscode/
.idea/
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
*~
# OS metadata
.DS_Store
Thumbs.db
# Misc secrets/keys (if present)
*.pem
*.key
*.crt
*.p12
*.pfx
# Package tarballs
*.tgz

View File

@@ -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
```
![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
@@ -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,12 +144,8 @@ 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
- Powered by [strfry](https://github.com/hoytech/strfry)
- Lightning Network integration via [LNbits](https://lnbits.com)
- Lightning Network integration via [LNbits](https://lnbits.com)

View File

@@ -45,4 +45,5 @@ We reserve the right to terminate or suspend access to our service immediately,
## 8. Changes to Terms
We reserve the right to modify these terms at any time. We will notify users of any changes by updating the date at the top of this page.
We reserve the right to modify these terms at any time. We will notify users of any changes by updating the date at the top of this page.

View File

@@ -11,7 +11,7 @@ export const apiService = {
'Content-Type': 'application/json',
},
body: JSON.stringify({
identifier: pubkey,
npub: pubkey,
}),
});
@@ -48,7 +48,7 @@ export const apiService = {
'X-Api-Key': apiKey,
},
body: JSON.stringify({
identifier: pubkey,
npub: pubkey,
}),
});

View File

@@ -8,6 +8,7 @@ const api = axios.create({
headers: {
'X-Api-Key': API_KEY,
'Content-Type': 'application/json',
Accept: 'application/json',
},
});
@@ -62,16 +63,26 @@ export const lnbitsService = {
extra = {},
}: CreateInvoiceParams): Promise<Invoice> {
try {
const response = await api.post('/api/v1/payments', {
// Build a V1-safe payload: only include known/supported fields when defined
const payload: Record<string, any> = {
out: false,
amount,
memo,
unit,
webhook,
internal,
extra,
});
return response.data;
};
if (unit) payload.unit = unit;
if (webhook) payload.webhook = webhook;
if (typeof internal === 'boolean') payload.internal = internal;
if (extra && Object.keys(extra).length > 0) payload.extra = extra;
const response = await api.post('/api/v1/payments', payload);
const data = response.data ?? {};
// Normalize response to ensure payment_request is always populated for the UI
const normalized: Invoice = {
...data,
payment_request: data.payment_request || data.bolt11,
bolt11: data.bolt11 || data.payment_request,
};
return normalized;
} catch (error) {
console.error('Error creating invoice:', error);
throw error;
@@ -82,10 +93,13 @@ export const lnbitsService = {
async checkPayment(paymentHash: string): Promise<PaymentStatus> {
try {
const response = await api.get(`/api/v1/payments/${paymentHash}`);
const data = response.data || {};
// Normalize potential V1 shapes
const paid: boolean = (data.paid === true) || (data.status === 'paid') || (data.settled === true);
return {
paid: response.data.paid,
preimage: response.data.preimage,
details: response.data.details,
paid,
preimage: data.preimage,
details: data.details,
};
} catch (error) {
console.error('Error checking payment:', error);
@@ -150,17 +164,24 @@ export const lnbitsService = {
// Long poll payment status
async longPollPayment(paymentHash: string, timeout = 60000): Promise<boolean> {
try {
const response = await api.get(`/public/v1/payment/${paymentHash}`, {
timeout,
});
return response.data.paid;
} catch (error) {
if (axios.isAxiosError(error) && error.code === 'ECONNABORTED') {
return false; // Timeout reached
// Fallback to authenticated polling against V1 status endpoint to ensure compatibility
const start = Date.now();
const pollIntervalMs = 2000;
while (Date.now() - start < timeout) {
try {
const status = await this.checkPayment(paymentHash);
if (status.paid) return true;
} catch (error) {
// If transient error, keep polling until timeout
if (axios.isAxiosError(error) && (error.response?.status ?? 0) >= 500) {
// continue
} else {
console.error('Error polling payment:', error);
throw error;
}
}
console.error('Error polling payment:', error);
throw error;
await new Promise((r) => setTimeout(r, pollIntervalMs));
}
return false;
},
};