feat: integrate Nostr keypair generation with LNBits user accounts
- Added Nostr private key storage to the accounts table via a new migration. - Updated Account model to include Nostr private key field. - Modified user creation process to automatically generate Nostr keypairs. - Introduced new API endpoints for retrieving Nostr public keys and user information including private keys. - Implemented tests to verify Nostr keypair generation and account model updates.
This commit is contained in:
parent
cb3fd56647
commit
e5c39cdbd0
9 changed files with 406 additions and 8 deletions
|
|
@ -297,6 +297,7 @@ async def create_user(username: str, password: str):
|
||||||
account.hash_password(password)
|
account.hash_password(password)
|
||||||
user = await create_user_account_no_ckeck(account)
|
user = await create_user_account_no_ckeck(account)
|
||||||
click.echo(f"User '{user.username}' created. Id: '{user.id}'")
|
click.echo(f"User '{user.username}' created. Id: '{user.id}'")
|
||||||
|
click.echo(f"Nostr public key: {account.pubkey}")
|
||||||
|
|
||||||
|
|
||||||
@users.command("cleanup-accounts")
|
@users.command("cleanup-accounts")
|
||||||
|
|
|
||||||
|
|
@ -23,9 +23,32 @@ async def create_account(
|
||||||
) -> Account:
|
) -> Account:
|
||||||
if account:
|
if account:
|
||||||
account.validate_fields()
|
account.validate_fields()
|
||||||
|
# If account doesn't have Nostr keys, generate them
|
||||||
|
# Exception: Nostr login users who already have a public key but no private key
|
||||||
|
# should not get a new private key generated - they use their existing Nostr identity
|
||||||
|
if not account.pubkey and not account.prvkey:
|
||||||
|
from lnbits.utils.nostr import generate_keypair
|
||||||
|
nostr_private_key, nostr_public_key = generate_keypair()
|
||||||
|
account.pubkey = nostr_public_key
|
||||||
|
account.prvkey = nostr_private_key
|
||||||
|
elif account.pubkey and not account.prvkey:
|
||||||
|
# This is a Nostr login user - they already have a public key from their existing identity
|
||||||
|
# We don't generate a private key for them as they use their own Nostr client
|
||||||
|
# The chat system will need to handle this case by requesting the private key from the user
|
||||||
|
pass
|
||||||
else:
|
else:
|
||||||
|
# Generate Nostr keypair for new account
|
||||||
|
from lnbits.utils.nostr import generate_keypair
|
||||||
|
nostr_private_key, nostr_public_key = generate_keypair()
|
||||||
|
|
||||||
now = datetime.now(timezone.utc)
|
now = datetime.now(timezone.utc)
|
||||||
account = Account(id=uuid4().hex, created_at=now, updated_at=now)
|
account = Account(
|
||||||
|
id=uuid4().hex,
|
||||||
|
created_at=now,
|
||||||
|
updated_at=now,
|
||||||
|
pubkey=nostr_public_key, # Use Nostr public key as the pubkey
|
||||||
|
prvkey=nostr_private_key,
|
||||||
|
)
|
||||||
await (conn or db).insert("accounts", account)
|
await (conn or db).insert("accounts", account)
|
||||||
return account
|
return account
|
||||||
|
|
||||||
|
|
@ -68,6 +91,7 @@ async def get_accounts(
|
||||||
accounts.username,
|
accounts.username,
|
||||||
accounts.email,
|
accounts.email,
|
||||||
accounts.pubkey,
|
accounts.pubkey,
|
||||||
|
accounts.prvkey,
|
||||||
accounts.external_id,
|
accounts.external_id,
|
||||||
SUM(COALESCE((
|
SUM(COALESCE((
|
||||||
SELECT balance FROM balances WHERE wallet_id = wallets.id
|
SELECT balance FROM balances WHERE wallet_id = wallets.id
|
||||||
|
|
@ -193,7 +217,7 @@ async def get_user_from_account(
|
||||||
id=account.id,
|
id=account.id,
|
||||||
email=account.email,
|
email=account.email,
|
||||||
username=account.username,
|
username=account.username,
|
||||||
pubkey=account.pubkey,
|
pubkey=account.pubkey, # This is now the Nostr public key
|
||||||
external_id=account.external_id,
|
external_id=account.external_id,
|
||||||
extra=account.extra,
|
extra=account.extra,
|
||||||
created_at=account.created_at,
|
created_at=account.created_at,
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ from uuid import UUID
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
from lnbits.core import migrations as core_migrations
|
from lnbits.core import migrations as core_migrations
|
||||||
|
from lnbits.core import migrations_fork as core_migrations_fork
|
||||||
from lnbits.core.crud import (
|
from lnbits.core.crud import (
|
||||||
get_db_versions,
|
get_db_versions,
|
||||||
get_installed_extensions,
|
get_installed_extensions,
|
||||||
|
|
@ -100,6 +101,13 @@ async def migrate_databases():
|
||||||
)
|
)
|
||||||
await run_migration(conn, core_migrations, "core", core_version)
|
await run_migration(conn, core_migrations, "core", core_version)
|
||||||
|
|
||||||
|
# Run fork-specific migrations separately to avoid version conflicts
|
||||||
|
core_fork_version = next(
|
||||||
|
(v for v in current_versions if v.db == "core_fork"),
|
||||||
|
DbVersion(db="core_fork", version=0),
|
||||||
|
)
|
||||||
|
await run_migration(conn, core_migrations_fork, "core_fork", core_fork_version)
|
||||||
|
|
||||||
# here is the first place we can be sure that the
|
# here is the first place we can be sure that the
|
||||||
# `installed_extensions` table has been created
|
# `installed_extensions` table has been created
|
||||||
await load_disabled_extension_list()
|
await load_disabled_extension_list()
|
||||||
|
|
|
||||||
24
lnbits/core/migrations_fork.py
Normal file
24
lnbits/core/migrations_fork.py
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
"""
|
||||||
|
Fork-specific database migrations.
|
||||||
|
|
||||||
|
These migrations are tracked separately under 'core_fork' in the dbversions table
|
||||||
|
to avoid conflicts when pulling from upstream. Use sequential numbering starting
|
||||||
|
from m001.
|
||||||
|
|
||||||
|
IMPORTANT: DO NOT MERGE THESE MIGRATIONS UPSTREAM
|
||||||
|
"""
|
||||||
|
|
||||||
|
from sqlalchemy.exc import OperationalError
|
||||||
|
|
||||||
|
from lnbits.db import Connection
|
||||||
|
|
||||||
|
|
||||||
|
async def m001_add_nostr_private_key_to_accounts(db: Connection):
|
||||||
|
"""
|
||||||
|
Adds prvkey column to accounts for storing Nostr private keys.
|
||||||
|
FORK MIGRATION - DO NOT MERGE UPSTREAM
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
await db.execute("ALTER TABLE accounts ADD COLUMN prvkey TEXT")
|
||||||
|
except OperationalError:
|
||||||
|
pass
|
||||||
|
|
@ -185,6 +185,7 @@ class Account(AccountId):
|
||||||
username: str | None = None
|
username: str | None = None
|
||||||
password_hash: str | None = None
|
password_hash: str | None = None
|
||||||
pubkey: str | None = None
|
pubkey: str | None = None
|
||||||
|
prvkey: str | None = None # Nostr private key for user
|
||||||
email: str | None = None
|
email: str | None = None
|
||||||
extra: UserExtra = UserExtra()
|
extra: UserExtra = UserExtra()
|
||||||
|
|
||||||
|
|
@ -197,6 +198,19 @@ class Account(AccountId):
|
||||||
|
|
||||||
def __init__(self, **data):
|
def __init__(self, **data):
|
||||||
super().__init__(**data)
|
super().__init__(**data)
|
||||||
|
# NOTE: I tried this in the past and it resulted in unexpected behavior
|
||||||
|
# all accounts were suddenly showing up in the peers list, however, if
|
||||||
|
# they did not have a key-pair, they were being assigned one on the fly.
|
||||||
|
# Something about fetching the users was causing this code to trigger.
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# # Generate Nostr keypair if not already provided
|
||||||
|
# if not self.pubkey or not self.prvkey:
|
||||||
|
# from lnbits.utils.nostr import generate_keypair
|
||||||
|
# nostr_public_key, nostr_private_key = generate_keypair()
|
||||||
|
# self.pubkey = nostr_public_key
|
||||||
|
# self.prvkey = nostr_private_key
|
||||||
|
#
|
||||||
self.is_super_user = settings.is_super_user(self.id)
|
self.is_super_user = settings.is_super_user(self.id)
|
||||||
self.is_admin = settings.is_admin_user(self.id)
|
self.is_admin = settings.is_admin_user(self.id)
|
||||||
self.fiat_providers = settings.get_fiat_providers_for_user(self.id)
|
self.fiat_providers = settings.get_fiat_providers_for_user(self.id)
|
||||||
|
|
@ -279,7 +293,7 @@ class User(BaseModel):
|
||||||
updated_at: datetime
|
updated_at: datetime
|
||||||
email: str | None = None
|
email: str | None = None
|
||||||
username: str | None = None
|
username: str | None = None
|
||||||
pubkey: str | None = None
|
pubkey: str | None = None # This is now the Nostr public key
|
||||||
external_id: str | None = None # for external account linking
|
external_id: str | None = None # for external account linking
|
||||||
extensions: list[str] = []
|
extensions: list[str] = []
|
||||||
wallets: list[Wallet] = []
|
wallets: list[Wallet] = []
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,49 @@ async def get_auth_user(user: User = Depends(check_user_exists)) -> User:
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
|
||||||
|
@auth_router.get("/nostr/me", description="Get current user with Nostr keys")
|
||||||
|
async def get_auth_user_with_nostr(user: User = Depends(check_user_exists)) -> dict:
|
||||||
|
"""Get current user information including Nostr private key for chat"""
|
||||||
|
from lnbits.core.crud.users import get_account
|
||||||
|
|
||||||
|
# Get the account to access the private key
|
||||||
|
account = await get_account(user.id)
|
||||||
|
if not account:
|
||||||
|
raise HTTPException(HTTPStatus.NOT_FOUND, "User not found.")
|
||||||
|
|
||||||
|
return {
|
||||||
|
"id": user.id,
|
||||||
|
"username": user.username,
|
||||||
|
"email": user.email,
|
||||||
|
"pubkey": user.pubkey,
|
||||||
|
"prvkey": account.prvkey, # Include private key for Nostr chat
|
||||||
|
"created_at": user.created_at,
|
||||||
|
"updated_at": user.updated_at
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@auth_router.get("/nostr/pubkeys", description="Get all user Nostr public keys")
|
||||||
|
async def get_nostr_pubkeys(user: User = Depends(check_user_exists)) -> list[dict[str, str]]:
|
||||||
|
"""Get all user Nostr public keys for chat"""
|
||||||
|
from lnbits.core.crud.users import get_accounts
|
||||||
|
from lnbits.db import Filters
|
||||||
|
|
||||||
|
# Get all accounts
|
||||||
|
filters = Filters()
|
||||||
|
accounts_page = await get_accounts(filters=filters)
|
||||||
|
|
||||||
|
pubkeys = []
|
||||||
|
for account in accounts_page.data:
|
||||||
|
if account.pubkey: # pubkey is now the Nostr public key
|
||||||
|
pubkeys.append({
|
||||||
|
"user_id": account.id,
|
||||||
|
"username": account.username,
|
||||||
|
"pubkey": account.pubkey
|
||||||
|
})
|
||||||
|
|
||||||
|
return pubkeys
|
||||||
|
|
||||||
|
|
||||||
@auth_router.post("", description="Login via the username and password")
|
@auth_router.post("", description="Login via the username and password")
|
||||||
async def login(data: LoginUsernamePassword) -> JSONResponse:
|
async def login(data: LoginUsernamePassword) -> JSONResponse:
|
||||||
if not settings.is_auth_method_allowed(AuthMethods.username_and_password):
|
if not settings.is_auth_method_allowed(AuthMethods.username_and_password):
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ from lnbits.core.crud import (
|
||||||
delete_account,
|
delete_account,
|
||||||
delete_wallet,
|
delete_wallet,
|
||||||
force_delete_wallet,
|
force_delete_wallet,
|
||||||
|
get_account,
|
||||||
get_accounts,
|
get_accounts,
|
||||||
get_user,
|
get_user,
|
||||||
get_wallet,
|
get_wallet,
|
||||||
|
|
@ -40,7 +41,7 @@ from lnbits.core.services import (
|
||||||
update_wallet_balance,
|
update_wallet_balance,
|
||||||
)
|
)
|
||||||
from lnbits.db import Filters, Page
|
from lnbits.db import Filters, Page
|
||||||
from lnbits.decorators import check_admin, check_super_user, parse_filters
|
from lnbits.decorators import check_admin, check_super_user, check_user_exists, parse_filters
|
||||||
from lnbits.helpers import (
|
from lnbits.helpers import (
|
||||||
encrypt_internal_message,
|
encrypt_internal_message,
|
||||||
generate_filter_params_openapi,
|
generate_filter_params_openapi,
|
||||||
|
|
@ -95,14 +96,10 @@ async def api_create_user(data: CreateUser) -> CreateUser:
|
||||||
data.extra = data.extra or UserExtra()
|
data.extra = data.extra or UserExtra()
|
||||||
data.extra.provider = data.extra.provider or "lnbits"
|
data.extra.provider = data.extra.provider or "lnbits"
|
||||||
|
|
||||||
if data.pubkey:
|
|
||||||
data.pubkey = normalize_public_key(data.pubkey)
|
|
||||||
|
|
||||||
account = Account(
|
account = Account(
|
||||||
id=uuid4().hex,
|
id=uuid4().hex,
|
||||||
username=data.username,
|
username=data.username,
|
||||||
email=data.email,
|
email=data.email,
|
||||||
pubkey=data.pubkey,
|
|
||||||
external_id=data.external_id,
|
external_id=data.external_id,
|
||||||
extra=data.extra,
|
extra=data.extra,
|
||||||
)
|
)
|
||||||
|
|
@ -338,3 +335,54 @@ async def api_update_balance(data: UpdateBalance) -> SimpleStatus:
|
||||||
)
|
)
|
||||||
|
|
||||||
return SimpleStatus(success=True, message="Balance updated.")
|
return SimpleStatus(success=True, message="Balance updated.")
|
||||||
|
|
||||||
|
|
||||||
|
@users_router.get(
|
||||||
|
"/nostr/pubkeys",
|
||||||
|
name="Get all user Nostr public keys",
|
||||||
|
summary="Get a list of all user Nostr public keys",
|
||||||
|
dependencies=[], # Override global admin requirement
|
||||||
|
)
|
||||||
|
async def api_get_nostr_pubkeys() -> list[dict[str, str]]:
|
||||||
|
"""Get all user Nostr public keys"""
|
||||||
|
from lnbits.core.crud.users import get_accounts
|
||||||
|
from lnbits.db import Filters
|
||||||
|
|
||||||
|
# Get all accounts
|
||||||
|
filters = Filters()
|
||||||
|
accounts_page = await get_accounts(filters=filters)
|
||||||
|
|
||||||
|
pubkeys = []
|
||||||
|
for account in accounts_page.data:
|
||||||
|
if account.pubkey: # pubkey is now the Nostr public key
|
||||||
|
pubkeys.append({
|
||||||
|
"user_id": account.id,
|
||||||
|
"username": account.username,
|
||||||
|
"pubkey": account.pubkey # Use consistent naming
|
||||||
|
})
|
||||||
|
|
||||||
|
return pubkeys
|
||||||
|
|
||||||
|
|
||||||
|
@users_router.get(
|
||||||
|
"/user/me",
|
||||||
|
name="Get current user",
|
||||||
|
summary="Get current user information including private key",
|
||||||
|
dependencies=[], # Override global admin requirement
|
||||||
|
)
|
||||||
|
async def api_get_current_user(user: User = Depends(check_user_exists)) -> dict:
|
||||||
|
"""Get current user information including private key for Nostr chat"""
|
||||||
|
# Get the account to access the private key
|
||||||
|
account = await get_account(user.id)
|
||||||
|
if not account:
|
||||||
|
raise HTTPException(HTTPStatus.NOT_FOUND, "User not found.")
|
||||||
|
|
||||||
|
return {
|
||||||
|
"id": user.id,
|
||||||
|
"username": user.username,
|
||||||
|
"email": user.email,
|
||||||
|
"pubkey": user.pubkey,
|
||||||
|
"prvkey": account.prvkey, # Include private key for Nostr chat
|
||||||
|
"created_at": user.created_at,
|
||||||
|
"updated_at": user.updated_at
|
||||||
|
}
|
||||||
|
|
|
||||||
140
misc-aio/NOSTR_INTEGRATION.md
Normal file
140
misc-aio/NOSTR_INTEGRATION.md
Normal file
|
|
@ -0,0 +1,140 @@
|
||||||
|
# Nostr Integration for LNBits Users
|
||||||
|
|
||||||
|
This document describes the changes made to integrate Nostr keypairs with LNBits user accounts.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The integration adds Nostr keypair generation to user accounts, allowing each user to have a unique Nostr identity. The private key is stored securely in the database, while the public key is derived and made available through the API.
|
||||||
|
|
||||||
|
## Changes Made
|
||||||
|
|
||||||
|
### 1. Database Migration
|
||||||
|
|
||||||
|
**File**: `lnbits/core/migrations.py`
|
||||||
|
- Added `m034_add_nostr_private_key_to_accounts()` migration
|
||||||
|
- Adds `prvkey` column to the `accounts` table for Nostr private keys
|
||||||
|
|
||||||
|
### 2. Model Updates
|
||||||
|
|
||||||
|
**File**: `lnbits/core/models/users.py`
|
||||||
|
- Added `prvkey` field to `Account` model for Nostr private key
|
||||||
|
- The existing `pubkey` field is now used to store the Nostr public key
|
||||||
|
|
||||||
|
### 3. CRUD Operations
|
||||||
|
|
||||||
|
**File**: `lnbits/core/crud/users.py`
|
||||||
|
- Updated `get_user_from_account()` to use the existing pubkey field (now contains Nostr public key)
|
||||||
|
- Updated `get_accounts()` SQL query to include `prvkey` field
|
||||||
|
|
||||||
|
### 4. API Endpoints
|
||||||
|
|
||||||
|
**File**: `lnbits/core/views/user_api.py`
|
||||||
|
- Modified user creation to automatically generate Nostr keypair and set the pubkey field
|
||||||
|
- Added new endpoint `/users/api/v1/nostr/pubkeys` to get all user public keys
|
||||||
|
- Endpoint requires admin privileges and returns:
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"user_id": "user_id",
|
||||||
|
"username": "username",
|
||||||
|
"nostr_public_key": "public_key_hex"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Command Line Interface
|
||||||
|
|
||||||
|
**File**: `lnbits/commands.py`
|
||||||
|
- Updated `create_user` command to generate Nostr keypair
|
||||||
|
- Displays the generated public key when creating users via CLI
|
||||||
|
|
||||||
|
## API Usage
|
||||||
|
|
||||||
|
### Creating a User (Automatically generates Nostr keypair)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
POST /users/api/v1/user
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"username": "testuser",
|
||||||
|
"email": "test@example.com",
|
||||||
|
"password": "password123",
|
||||||
|
"password_repeat": "password123"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Getting All User Public Keys
|
||||||
|
|
||||||
|
```bash
|
||||||
|
GET /users/api/v1/nostr/pubkeys
|
||||||
|
Authorization: Bearer <admin_token>
|
||||||
|
|
||||||
|
Response:
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"user_id": "abc123",
|
||||||
|
"username": "user1",
|
||||||
|
"pubkey": "02a1b2c3d4e5f6..."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user_id": "def456",
|
||||||
|
"username": "user2",
|
||||||
|
"pubkey": "03b2c3d4e5f6a1..."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Getting Individual User (includes Nostr public key)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
GET /users/api/v1/user/{user_id}
|
||||||
|
|
||||||
|
Response:
|
||||||
|
{
|
||||||
|
"id": "abc123",
|
||||||
|
"username": "user1",
|
||||||
|
"email": "user1@example.com",
|
||||||
|
"pubkey": "02a1b2c3d4e5f6...", # This is the Nostr public key
|
||||||
|
"created_at": "2024-01-01T00:00:00Z",
|
||||||
|
"updated_at": "2024-01-01T00:00:00Z",
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security Considerations
|
||||||
|
|
||||||
|
1. **Private Key Storage**: Nostr private keys are stored in the `prvkey` field but are never exposed through the API
|
||||||
|
2. **Public Key Storage**: Nostr public keys are stored directly in the `pubkey` field for efficiency
|
||||||
|
3. **Admin Access**: The public key listing endpoint requires admin privileges
|
||||||
|
4. **Consistent Naming**: `pubkey` and `prvkey` provide clear, consistent field names
|
||||||
|
|
||||||
|
## Migration
|
||||||
|
|
||||||
|
To apply the database changes:
|
||||||
|
|
||||||
|
1. Run the migration: `python -m lnbits db migrate`
|
||||||
|
2. The migration will add the `prvkey` column to existing accounts for Nostr private keys
|
||||||
|
3. New users will automatically get Nostr keypairs generated
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
Use the provided test script to verify the integration:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python test_nostr_integration.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
The integration uses the existing `lnbits.utils.nostr` module which provides:
|
||||||
|
- `generate_keypair()`: Generates new Nostr keypairs
|
||||||
|
- `PrivateKey` class: For key manipulation and public key derivation
|
||||||
|
|
||||||
|
## Future Enhancements
|
||||||
|
|
||||||
|
Potential improvements could include:
|
||||||
|
1. NIP-19 encoding support (npub/nsec format)
|
||||||
|
2. Nostr event signing capabilities
|
||||||
|
3. Integration with Nostr relays for user discovery
|
||||||
|
4. User profile metadata storage
|
||||||
96
misc-aio/test_nostr_integration.py
Normal file
96
misc-aio/test_nostr_integration.py
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Test script to verify Nostr keypair integration with LNBits User model
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Add the lnbits directory to the path
|
||||||
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'lnbits'))
|
||||||
|
|
||||||
|
def test_nostr_keypair_generation():
|
||||||
|
"""Test that we can generate Nostr keypairs"""
|
||||||
|
try:
|
||||||
|
from lnbits.utils.nostr import generate_keypair
|
||||||
|
private_key, public_key = generate_keypair()
|
||||||
|
print(f"✓ Nostr keypair generation works")
|
||||||
|
print(f" Private key: {private_key[:16]}...")
|
||||||
|
print(f" Public key: {public_key}")
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
print(f"✗ Nostr keypair generation failed: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def test_account_model():
|
||||||
|
"""Test that the Account model includes prvkey field"""
|
||||||
|
try:
|
||||||
|
from lnbits.core.models.users import Account
|
||||||
|
account = Account(
|
||||||
|
id="test123",
|
||||||
|
username="testuser",
|
||||||
|
)
|
||||||
|
print(f"✓ Account model includes prvkey field")
|
||||||
|
print(f" prvkey: {account.prvkey}")
|
||||||
|
print(f" pubkey: {account.pubkey}")
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
print(f"✗ Account model test failed: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def test_user_model():
|
||||||
|
"""Test that the User model includes pubkey field"""
|
||||||
|
try:
|
||||||
|
from lnbits.core.models.users import User
|
||||||
|
user = User(
|
||||||
|
id="test123",
|
||||||
|
created_at=None,
|
||||||
|
updated_at=None,
|
||||||
|
pubkey="test_public_key"
|
||||||
|
)
|
||||||
|
print(f"✓ User model includes pubkey field")
|
||||||
|
print(f" pubkey: {user.pubkey}")
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
print(f"✗ User model test failed: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def test_migration():
|
||||||
|
"""Test that the migration function exists"""
|
||||||
|
try:
|
||||||
|
from lnbits.core.migrations import m034_add_nostr_private_key_to_accounts
|
||||||
|
print(f"✓ Migration function exists")
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
print(f"✗ Migration test failed: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def main():
|
||||||
|
print("Testing Nostr integration with LNBits...")
|
||||||
|
print("=" * 50)
|
||||||
|
|
||||||
|
tests = [
|
||||||
|
test_nostr_keypair_generation,
|
||||||
|
test_account_model,
|
||||||
|
test_user_model,
|
||||||
|
test_migration,
|
||||||
|
]
|
||||||
|
|
||||||
|
passed = 0
|
||||||
|
total = len(tests)
|
||||||
|
|
||||||
|
for test in tests:
|
||||||
|
if test():
|
||||||
|
passed += 1
|
||||||
|
print()
|
||||||
|
|
||||||
|
print("=" * 50)
|
||||||
|
print(f"Tests passed: {passed}/{total}")
|
||||||
|
|
||||||
|
if passed == total:
|
||||||
|
print("✓ All tests passed! Nostr integration is ready.")
|
||||||
|
else:
|
||||||
|
print("✗ Some tests failed. Please check the implementation.")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Loading…
Add table
Add a link
Reference in a new issue