Refactor DCA API endpoints to require admin key for access: Updated wallet dependency in multiple DCA-related endpoints to use require_admin_key instead of require_invoice_key, enhancing security and access control. Cleaned up code formatting for improved readability.

Update DCA API calls to use admin key: Changed references from `inkey` to `adminkey` in multiple DCA-related API requests to ensure proper access control and security compliance.
This commit is contained in:
padreug 2025-06-22 11:13:41 +02:00
parent 931ae1308f
commit 466d2c74e3
2 changed files with 94 additions and 70 deletions

View file

@ -166,7 +166,7 @@ window.app = Vue.createApp({
const {data} = await LNbits.api.request( const {data} = await LNbits.api.request(
'GET', 'GET',
'/satmachineadmin/api/v1/dca/config', '/satmachineadmin/api/v1/dca/config',
this.g.user.wallets[0].inkey this.g.user.wallets[0].adminkey
) )
this.lamassuConfig = data this.lamassuConfig = data
@ -254,7 +254,7 @@ window.app = Vue.createApp({
const { data } = await LNbits.api.request( const { data } = await LNbits.api.request(
'GET', 'GET',
'/satmachineadmin/api/v1/dca/clients', '/satmachineadmin/api/v1/dca/clients',
this.g.user.wallets[0].inkey this.g.user.wallets[0].adminkey
) )
// Fetch balance data for each client // Fetch balance data for each client
@ -264,7 +264,7 @@ window.app = Vue.createApp({
const { data: balance } = await LNbits.api.request( const { data: balance } = await LNbits.api.request(
'GET', 'GET',
`/satmachineadmin/api/v1/dca/clients/${client.id}/balance`, `/satmachineadmin/api/v1/dca/clients/${client.id}/balance`,
this.g.user.wallets[0].inkey this.g.user.wallets[0].adminkey
) )
return { return {
...client, ...client,
@ -356,7 +356,7 @@ window.app = Vue.createApp({
const { data: balance } = await LNbits.api.request( const { data: balance } = await LNbits.api.request(
'GET', 'GET',
`/satmachineadmin/api/v1/dca/clients/${client.id}/balance`, `/satmachineadmin/api/v1/dca/clients/${client.id}/balance`,
this.g.user.wallets[0].inkey this.g.user.wallets[0].adminkey
) )
this.clientDetailsDialog.data = client this.clientDetailsDialog.data = client
this.clientDetailsDialog.balance = balance this.clientDetailsDialog.balance = balance
@ -372,7 +372,7 @@ window.app = Vue.createApp({
const { data } = await LNbits.api.request( const { data } = await LNbits.api.request(
'GET', 'GET',
'/satmachineadmin/api/v1/dca/deposits', '/satmachineadmin/api/v1/dca/deposits',
this.g.user.wallets[0].inkey this.g.user.wallets[0].adminkey
) )
this.deposits = data this.deposits = data
} catch (error) { } catch (error) {
@ -617,7 +617,7 @@ window.app = Vue.createApp({
const { data } = await LNbits.api.request( const { data } = await LNbits.api.request(
'GET', 'GET',
'/satmachineadmin/api/v1/dca/transactions', '/satmachineadmin/api/v1/dca/transactions',
this.g.user.wallets[0].inkey this.g.user.wallets[0].adminkey
) )
this.lamassuTransactions = data this.lamassuTransactions = data
} catch (error) { } catch (error) {
@ -630,7 +630,7 @@ window.app = Vue.createApp({
const { data: distributions } = await LNbits.api.request( const { data: distributions } = await LNbits.api.request(
'GET', 'GET',
`/satmachineadmin/api/v1/dca/transactions/${transaction.id}/distributions`, `/satmachineadmin/api/v1/dca/transactions/${transaction.id}/distributions`,
this.g.user.wallets[0].inkey this.g.user.wallets[0].adminkey
) )
this.distributionDialog.transaction = transaction this.distributionDialog.transaction = transaction

View file

@ -7,7 +7,7 @@ from fastapi import APIRouter, Depends, Request
from lnbits.core.crud import get_user from lnbits.core.crud import get_user
from lnbits.core.models import WalletTypeInfo from lnbits.core.models import WalletTypeInfo
from lnbits.core.services import create_invoice from lnbits.core.services import create_invoice
from lnbits.decorators import require_admin_key, require_invoice_key from lnbits.decorators import require_admin_key
from starlette.exceptions import HTTPException from starlette.exceptions import HTTPException
from .crud import ( from .crud import (
@ -32,15 +32,21 @@ from .crud import (
delete_lamassu_config, delete_lamassu_config,
# Lamassu transaction CRUD operations # Lamassu transaction CRUD operations
get_all_lamassu_transactions, get_all_lamassu_transactions,
get_lamassu_transaction get_lamassu_transaction,
) )
from .models import ( from .models import (
# DCA models # DCA models
CreateDcaClientData, DcaClient, UpdateDcaClientData, CreateDcaClientData,
CreateDepositData, DcaDeposit, UpdateDepositStatusData, DcaClient,
UpdateDcaClientData,
CreateDepositData,
DcaDeposit,
UpdateDepositStatusData,
ClientBalanceSummary, ClientBalanceSummary,
CreateLamassuConfigData, LamassuConfig, UpdateLamassuConfigData, CreateLamassuConfigData,
StoredLamassuTransaction LamassuConfig,
UpdateLamassuConfigData,
StoredLamassuTransaction,
) )
satmachineadmin_api_router = APIRouter() satmachineadmin_api_router = APIRouter()
@ -52,9 +58,10 @@ satmachineadmin_api_router = APIRouter()
# DCA Client Endpoints # DCA Client Endpoints
@satmachineadmin_api_router.get("/api/v1/dca/clients") @satmachineadmin_api_router.get("/api/v1/dca/clients")
async def api_get_dca_clients( async def api_get_dca_clients(
wallet: WalletTypeInfo = Depends(require_invoice_key), wallet: WalletTypeInfo = Depends(require_admin_key),
) -> list[DcaClient]: ) -> list[DcaClient]:
"""Get all DCA clients""" """Get all DCA clients"""
return await get_dca_clients() return await get_dca_clients()
@ -63,7 +70,7 @@ async def api_get_dca_clients(
@satmachineadmin_api_router.get("/api/v1/dca/clients/{client_id}") @satmachineadmin_api_router.get("/api/v1/dca/clients/{client_id}")
async def api_get_dca_client( async def api_get_dca_client(
client_id: str, client_id: str,
wallet: WalletTypeInfo = Depends(require_invoice_key), wallet: WalletTypeInfo = Depends(require_admin_key),
) -> DcaClient: ) -> DcaClient:
"""Get a specific DCA client""" """Get a specific DCA client"""
client = await get_dca_client(client_id) client = await get_dca_client(client_id)
@ -77,6 +84,7 @@ async def api_get_dca_client(
# Note: Client creation/update/delete will be handled by the DCA client extension # Note: Client creation/update/delete will be handled by the DCA client extension
# Admin extension only reads existing clients and manages their deposits # Admin extension only reads existing clients and manages their deposits
# TEMPORARY: Test client creation endpoint (remove in production) # TEMPORARY: Test client creation endpoint (remove in production)
@satmachineadmin_api_router.post("/api/v1/dca/clients", status_code=HTTPStatus.CREATED) @satmachineadmin_api_router.post("/api/v1/dca/clients", status_code=HTTPStatus.CREATED)
async def api_create_test_dca_client( async def api_create_test_dca_client(
@ -90,7 +98,7 @@ async def api_create_test_dca_client(
@satmachineadmin_api_router.get("/api/v1/dca/clients/{client_id}/balance") @satmachineadmin_api_router.get("/api/v1/dca/clients/{client_id}/balance")
async def api_get_client_balance( async def api_get_client_balance(
client_id: str, client_id: str,
wallet: WalletTypeInfo = Depends(require_invoice_key), wallet: WalletTypeInfo = Depends(require_admin_key),
) -> ClientBalanceSummary: ) -> ClientBalanceSummary:
"""Get client balance summary""" """Get client balance summary"""
client = await get_dca_client(client_id) client = await get_dca_client(client_id)
@ -104,9 +112,10 @@ async def api_get_client_balance(
# DCA Deposit Endpoints # DCA Deposit Endpoints
@satmachineadmin_api_router.get("/api/v1/dca/deposits") @satmachineadmin_api_router.get("/api/v1/dca/deposits")
async def api_get_deposits( async def api_get_deposits(
wallet: WalletTypeInfo = Depends(require_invoice_key), wallet: WalletTypeInfo = Depends(require_admin_key),
) -> list[DcaDeposit]: ) -> list[DcaDeposit]:
"""Get all deposits""" """Get all deposits"""
return await get_all_deposits() return await get_all_deposits()
@ -115,7 +124,7 @@ async def api_get_deposits(
@satmachineadmin_api_router.get("/api/v1/dca/deposits/{deposit_id}") @satmachineadmin_api_router.get("/api/v1/dca/deposits/{deposit_id}")
async def api_get_deposit( async def api_get_deposit(
deposit_id: str, deposit_id: str,
wallet: WalletTypeInfo = Depends(require_invoice_key), wallet: WalletTypeInfo = Depends(require_admin_key),
) -> DcaDeposit: ) -> DcaDeposit:
"""Get a specific deposit""" """Get a specific deposit"""
deposit = await get_deposit(deposit_id) deposit = await get_deposit(deposit_id)
@ -158,13 +167,15 @@ async def api_update_deposit_status(
updated_deposit = await update_deposit_status(deposit_id, data) updated_deposit = await update_deposit_status(deposit_id, data)
if not updated_deposit: if not updated_deposit:
raise HTTPException( raise HTTPException(
status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Failed to update deposit." status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
detail="Failed to update deposit.",
) )
return updated_deposit return updated_deposit
# Transaction Polling Endpoints # Transaction Polling Endpoints
@satmachineadmin_api_router.post("/api/v1/dca/test-connection") @satmachineadmin_api_router.post("/api/v1/dca/test-connection")
async def api_test_database_connection( async def api_test_database_connection(
wallet: WalletTypeInfo = Depends(require_admin_key), wallet: WalletTypeInfo = Depends(require_admin_key),
@ -184,7 +195,7 @@ async def api_test_database_connection(
"steps": [f"❌ Unexpected error: {str(e)}"], "steps": [f"❌ Unexpected error: {str(e)}"],
"ssh_tunnel_used": False, "ssh_tunnel_used": False,
"ssh_tunnel_success": False, "ssh_tunnel_success": False,
"database_connection_success": False "database_connection_success": False,
} }
@ -202,7 +213,7 @@ async def api_manual_poll(
if not db_config: if not db_config:
raise HTTPException( raise HTTPException(
status_code=HTTPStatus.SERVICE_UNAVAILABLE, status_code=HTTPStatus.SERVICE_UNAVAILABLE,
detail="Could not get Lamassu database configuration" detail="Could not get Lamassu database configuration",
) )
config_id = db_config["config_id"] config_id = db_config["config_id"]
@ -224,13 +235,13 @@ async def api_manual_poll(
return { return {
"success": True, "success": True,
"transactions_processed": transactions_processed, "transactions_processed": transactions_processed,
"message": f"Processed {transactions_processed} new transactions since last poll" "message": f"Processed {transactions_processed} new transactions since last poll",
} }
except Exception as e: except Exception as e:
raise HTTPException( raise HTTPException(
status_code=HTTPStatus.INTERNAL_SERVER_ERROR, status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
detail=f"Error during manual poll: {str(e)}" detail=f"Error during manual poll: {str(e)}",
) )
@ -258,7 +269,7 @@ async def api_test_transaction(
"crypto_code": "BTC", "crypto_code": "BTC",
"fiat_code": "GTQ", "fiat_code": "GTQ",
"device_id": "test_device", "device_id": "test_device",
"status": "confirmed" "status": "confirmed",
} }
# Process the mock transaction through the complete DCA flow # Process the mock transaction through the complete DCA flow
@ -281,24 +292,28 @@ async def api_test_transaction(
"total_amount_sats": crypto_atoms, "total_amount_sats": crypto_atoms,
"base_amount_sats": base_crypto_atoms, "base_amount_sats": base_crypto_atoms,
"commission_amount_sats": commission_amount_sats, "commission_amount_sats": commission_amount_sats,
"commission_percentage": commission_percentage * 100, # Show as percentage "commission_percentage": commission_percentage
"effective_commission": effective_commission * 100 if commission_percentage > 0 else 0, * 100, # Show as percentage
"discount": discount "effective_commission": effective_commission * 100
} if commission_percentage > 0
else 0,
"discount": discount,
},
} }
except Exception as e: except Exception as e:
raise HTTPException( raise HTTPException(
status_code=HTTPStatus.INTERNAL_SERVER_ERROR, status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
detail=f"Error processing test transaction: {str(e)}" detail=f"Error processing test transaction: {str(e)}",
) )
# Lamassu Transaction Endpoints # Lamassu Transaction Endpoints
@satmachineadmin_api_router.get("/api/v1/dca/transactions") @satmachineadmin_api_router.get("/api/v1/dca/transactions")
async def api_get_lamassu_transactions( async def api_get_lamassu_transactions(
wallet: WalletTypeInfo = Depends(require_invoice_key), wallet: WalletTypeInfo = Depends(require_admin_key),
) -> list[StoredLamassuTransaction]: ) -> list[StoredLamassuTransaction]:
"""Get all processed Lamassu transactions""" """Get all processed Lamassu transactions"""
return await get_all_lamassu_transactions() return await get_all_lamassu_transactions()
@ -307,7 +322,7 @@ async def api_get_lamassu_transactions(
@satmachineadmin_api_router.get("/api/v1/dca/transactions/{transaction_id}") @satmachineadmin_api_router.get("/api/v1/dca/transactions/{transaction_id}")
async def api_get_lamassu_transaction( async def api_get_lamassu_transaction(
transaction_id: str, transaction_id: str,
wallet: WalletTypeInfo = Depends(require_invoice_key), wallet: WalletTypeInfo = Depends(require_admin_key),
) -> StoredLamassuTransaction: ) -> StoredLamassuTransaction:
"""Get a specific Lamassu transaction with details""" """Get a specific Lamassu transaction with details"""
transaction = await get_lamassu_transaction(transaction_id) transaction = await get_lamassu_transaction(transaction_id)
@ -318,10 +333,12 @@ async def api_get_lamassu_transaction(
return transaction return transaction
@satmachineadmin_api_router.get("/api/v1/dca/transactions/{transaction_id}/distributions") @satmachineadmin_api_router.get(
"/api/v1/dca/transactions/{transaction_id}/distributions"
)
async def api_get_transaction_distributions( async def api_get_transaction_distributions(
transaction_id: str, transaction_id: str,
wallet: WalletTypeInfo = Depends(require_invoice_key), wallet: WalletTypeInfo = Depends(require_admin_key),
) -> list[dict]: ) -> list[dict]:
"""Get distribution details for a specific Lamassu transaction""" """Get distribution details for a specific Lamassu transaction"""
# Get the stored transaction # Get the stored transaction
@ -333,32 +350,38 @@ async def api_get_transaction_distributions(
# Get all DCA payments for this Lamassu transaction # Get all DCA payments for this Lamassu transaction
from .crud import get_payments_by_lamassu_transaction, get_dca_client from .crud import get_payments_by_lamassu_transaction, get_dca_client
payments = await get_payments_by_lamassu_transaction(transaction.lamassu_transaction_id)
payments = await get_payments_by_lamassu_transaction(
transaction.lamassu_transaction_id
)
# Enhance payments with client information # Enhance payments with client information
distributions = [] distributions = []
for payment in payments: for payment in payments:
client = await get_dca_client(payment.client_id) client = await get_dca_client(payment.client_id)
distributions.append({ distributions.append(
"payment_id": payment.id, {
"client_id": payment.client_id, "payment_id": payment.id,
"client_username": client.username if client else None, "client_id": payment.client_id,
"client_user_id": client.user_id if client else None, "client_username": client.username if client else None,
"amount_sats": payment.amount_sats, "client_user_id": client.user_id if client else None,
"amount_fiat": payment.amount_fiat, "amount_sats": payment.amount_sats,
"exchange_rate": payment.exchange_rate, "amount_fiat": payment.amount_fiat,
"status": payment.status, "exchange_rate": payment.exchange_rate,
"created_at": payment.created_at "status": payment.status,
}) "created_at": payment.created_at,
}
)
return distributions return distributions
# Lamassu Configuration Endpoints # Lamassu Configuration Endpoints
@satmachineadmin_api_router.get("/api/v1/dca/config") @satmachineadmin_api_router.get("/api/v1/dca/config")
async def api_get_lamassu_config( async def api_get_lamassu_config(
wallet: WalletTypeInfo = Depends(require_invoice_key), wallet: WalletTypeInfo = Depends(require_admin_key),
) -> Optional[LamassuConfig]: ) -> Optional[LamassuConfig]:
"""Get active Lamassu database configuration""" """Get active Lamassu database configuration"""
return await get_active_lamassu_config() return await get_active_lamassu_config()
@ -389,7 +412,8 @@ async def api_update_lamassu_config(
updated_config = await update_lamassu_config(config_id, data) updated_config = await update_lamassu_config(config_id, data)
if not updated_config: if not updated_config:
raise HTTPException( raise HTTPException(
status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Failed to update configuration." status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
detail="Failed to update configuration.",
) )
return updated_config return updated_config