Refactor DCA API endpoints to use superuser authentication: Updated all relevant DCA-related API endpoints to require check_super_user instead of require_admin_key, enhancing security. Adjusted client-side API calls to remove wallet admin key usage, ensuring session-based superuser authentication is utilized. Updated documentation in CLAUDE.md to reflect these changes.

This commit is contained in:
padreug 2025-06-26 13:18:01 +02:00
parent dfc2dd695c
commit 8871f24cec
4 changed files with 87 additions and 84 deletions

View file

@ -68,7 +68,8 @@ The Satoshi Machine Admin extension follows LNBits architecture patterns:
- Use `:attribute='value'` for binding, `v-html='value'` for HTML content - Use `:attribute='value'` for binding, `v-html='value'` for HTML content
2. **API Patterns**: 2. **API Patterns**:
- Always include wallet key (inkey/adminkey) as third parameter in API calls - Admin extension uses session-based superuser authentication (no API keys)
- Client extension uses wallet admin keys for user-specific operations
- Use `LNbits.api.request()` for all API calls - Use `LNbits.api.request()` for all API calls
- Destructure responses: `const {data} = await LNbits.api.request(...)` - Destructure responses: `const {data} = await LNbits.api.request(...)`
@ -79,10 +80,12 @@ The Satoshi Machine Admin extension follows LNBits architecture patterns:
### The Magical G Object ### The Magical G Object
The global `this.g` object provides access to: The global `this.g` object provides access to:
- `this.g.user` - Complete user data including wallets array - `this.g.user` - Complete user data including wallets array
- `this.g.user.wallets[0].inkey` - Invoice key for API calls - `this.g.user.wallets[0].inkey` - Invoice key (client extension only)
- `this.g.user.wallets[0].adminkey` - Admin key for privileged operations - `this.g.user.wallets[0].adminkey` - Admin key (client extension only)
- `this.g.wallet` - Currently selected wallet - `this.g.wallet` - Currently selected wallet
**Note**: Admin extension uses superuser session authentication, not wallet keys.
### Built-in Utilities ### Built-in Utilities
- Currency conversion: `/api/v1/currencies`, `/api/v1/conversion` - Currency conversion: `/api/v1/currencies`, `/api/v1/conversion`
- QR code generation: `/api/v1/qrcode/{data}` or Quasar VueQrcode component - QR code generation: `/api/v1/qrcode/{data}` or Quasar VueQrcode component
@ -208,9 +211,11 @@ commission_amount = 266800 - 258835 = 7,965 sats (to commission wallet)
- **Error Handling**: Graceful failure with detailed logging - **Error Handling**: Graceful failure with detailed logging
### Security Considerations ### Security Considerations
- **Superuser Authentication**: Admin extension requires LNBits superuser login
- **Wallet Admin Keys**: Client extension uses wallet admin keys for user operations
- **Database Access**: Only superusers can write to satoshimachine database
- SSH tunnel encryption for database connectivity - SSH tunnel encryption for database connectivity
- Read-only database permissions - Read-only database permissions for Lamassu access
- Wallet key validation for all financial operations
- Input sanitization and type validation - Input sanitization and type validation
- Audit logging for all administrative actions - Audit logging for all administrative actions

View file

@ -86,14 +86,14 @@ window.app = Vue.createApp({
amount: null, amount: null,
notes: '' notes: ''
}, },
// Polling status // Polling status
lastPollTime: null, lastPollTime: null,
testingConnection: false, testingConnection: false,
runningManualPoll: false, runningManualPoll: false,
runningTestTransaction: false, runningTestTransaction: false,
lamassuConfig: null, lamassuConfig: null,
// Config dialog // Config dialog
configDialog: { configDialog: {
show: false, show: false,
@ -163,13 +163,13 @@ window.app = Vue.createApp({
// Configuration Methods // Configuration Methods
async getLamassuConfig() { async getLamassuConfig() {
try { try {
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].adminkey null
) )
this.lamassuConfig = data this.lamassuConfig = data
// When opening config dialog, populate the selected wallets if they exist // When opening config dialog, populate the selected wallets if they exist
if (data && data.source_wallet_id) { if (data && data.source_wallet_id) {
const wallet = this.g.user.wallets.find(w => w.id === data.source_wallet_id) const wallet = this.g.user.wallets.find(w => w.id === data.source_wallet_id)
@ -188,7 +188,7 @@ window.app = Vue.createApp({
this.lamassuConfig = null this.lamassuConfig = null
} }
}, },
async saveConfiguration() { async saveConfiguration() {
try { try {
const data = { const data = {
@ -207,17 +207,17 @@ window.app = Vue.createApp({
ssh_password: this.configDialog.data.ssh_password, ssh_password: this.configDialog.data.ssh_password,
ssh_private_key: this.configDialog.data.ssh_private_key ssh_private_key: this.configDialog.data.ssh_private_key
} }
const {data: config} = await LNbits.api.request( const { data: config } = await LNbits.api.request(
'POST', 'POST',
'/satmachineadmin/api/v1/dca/config', '/satmachineadmin/api/v1/dca/config',
this.g.user.wallets[0].adminkey, null,
data data
) )
this.lamassuConfig = config this.lamassuConfig = config
this.closeConfigDialog() this.closeConfigDialog()
this.$q.notify({ this.$q.notify({
type: 'positive', type: 'positive',
message: 'Database configuration saved successfully', message: 'Database configuration saved successfully',
@ -227,7 +227,7 @@ window.app = Vue.createApp({
LNbits.utils.notifyApiError(error) LNbits.utils.notifyApiError(error)
} }
}, },
closeConfigDialog() { closeConfigDialog() {
this.configDialog.show = false this.configDialog.show = false
this.configDialog.data = { this.configDialog.data = {
@ -254,9 +254,9 @@ 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].adminkey null
) )
// Fetch balance data for each client // Fetch balance data for each client
const clientsWithBalances = await Promise.all( const clientsWithBalances = await Promise.all(
data.map(async (client) => { data.map(async (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].adminkey null
) )
return { return {
...client, ...client,
@ -279,7 +279,7 @@ window.app = Vue.createApp({
} }
}) })
) )
this.dcaClients = clientsWithBalances this.dcaClients = clientsWithBalances
} catch (error) { } catch (error) {
LNbits.utils.notifyApiError(error) LNbits.utils.notifyApiError(error)
@ -300,7 +300,7 @@ window.app = Vue.createApp({
const { data: newDeposit } = await LNbits.api.request( const { data: newDeposit } = await LNbits.api.request(
'POST', 'POST',
'/satmachineadmin/api/v1/dca/deposits', '/satmachineadmin/api/v1/dca/deposits',
this.g.user.wallets[0].adminkey, null,
data data
) )
@ -328,7 +328,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].adminkey null
) )
this.clientDetailsDialog.data = client this.clientDetailsDialog.data = client
this.clientDetailsDialog.balance = balance this.clientDetailsDialog.balance = balance
@ -344,7 +344,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].adminkey null
) )
this.deposits = data this.deposits = data
} catch (error) { } catch (error) {
@ -375,7 +375,7 @@ window.app = Vue.createApp({
const { data: updatedDeposit } = await LNbits.api.request( const { data: updatedDeposit } = await LNbits.api.request(
'PUT', 'PUT',
`/satmachineadmin/api/v1/dca/deposits/${this.depositFormDialog.data.id}`, `/satmachineadmin/api/v1/dca/deposits/${this.depositFormDialog.data.id}`,
this.g.user.wallets[0].adminkey, null,
{ status: this.depositFormDialog.data.status, notes: data.notes } { status: this.depositFormDialog.data.status, notes: data.notes }
) )
const index = this.deposits.findIndex(d => d.id === updatedDeposit.id) const index = this.deposits.findIndex(d => d.id === updatedDeposit.id)
@ -387,7 +387,7 @@ window.app = Vue.createApp({
const { data: newDeposit } = await LNbits.api.request( const { data: newDeposit } = await LNbits.api.request(
'POST', 'POST',
'/satmachineadmin/api/v1/dca/deposits', '/satmachineadmin/api/v1/dca/deposits',
this.g.user.wallets[0].adminkey, null,
data data
) )
this.deposits.unshift(newDeposit) this.deposits.unshift(newDeposit)
@ -419,7 +419,7 @@ window.app = Vue.createApp({
const { data: updatedDeposit } = await LNbits.api.request( const { data: updatedDeposit } = await LNbits.api.request(
'PUT', 'PUT',
`/satmachineadmin/api/v1/dca/deposits/${deposit.id}/status`, `/satmachineadmin/api/v1/dca/deposits/${deposit.id}/status`,
this.g.user.wallets[0].adminkey, null,
{ status: 'confirmed', notes: 'Confirmed by admin - money placed in machine' } { status: 'confirmed', notes: 'Confirmed by admin - money placed in machine' }
) )
const index = this.deposits.findIndex(d => d.id === deposit.id) const index = this.deposits.findIndex(d => d.id === deposit.id)
@ -454,30 +454,30 @@ window.app = Vue.createApp({
async exportLamassuTransactionsCSV() { async exportLamassuTransactionsCSV() {
await LNbits.utils.exportCSV(this.lamassuTransactionsTable.columns, this.lamassuTransactions) await LNbits.utils.exportCSV(this.lamassuTransactionsTable.columns, this.lamassuTransactions)
}, },
// Polling Methods // Polling Methods
async testDatabaseConnection() { async testDatabaseConnection() {
this.testingConnection = true this.testingConnection = true
try { try {
const {data} = await LNbits.api.request( const { data } = await LNbits.api.request(
'POST', 'POST',
'/satmachineadmin/api/v1/dca/test-connection', '/satmachineadmin/api/v1/dca/test-connection',
this.g.user.wallets[0].adminkey null
) )
// Show detailed results in a dialog // Show detailed results in a dialog
const stepsList = data.steps ? data.steps.join('\n') : 'No detailed steps available' const stepsList = data.steps ? data.steps.join('\n') : 'No detailed steps available'
let dialogContent = `<strong>Connection Test Results</strong><br/><br/>` let dialogContent = `<strong>Connection Test Results</strong><br/><br/>`
if (data.ssh_tunnel_used) { if (data.ssh_tunnel_used) {
dialogContent += `<strong>SSH Tunnel:</strong> ${data.ssh_tunnel_success ? '✅ Success' : '❌ Failed'}<br/>` dialogContent += `<strong>SSH Tunnel:</strong> ${data.ssh_tunnel_success ? '✅ Success' : '❌ Failed'}<br/>`
} }
dialogContent += `<strong>Database:</strong> ${data.database_connection_success ? '✅ Success' : '❌ Failed'}<br/><br/>` dialogContent += `<strong>Database:</strong> ${data.database_connection_success ? '✅ Success' : '❌ Failed'}<br/><br/>`
dialogContent += `<strong>Detailed Steps:</strong><br/>` dialogContent += `<strong>Detailed Steps:</strong><br/>`
dialogContent += stepsList.replace(/\n/g, '<br/>') dialogContent += stepsList.replace(/\n/g, '<br/>')
this.$q.dialog({ this.$q.dialog({
title: data.success ? 'Connection Test Passed' : 'Connection Test Failed', title: data.success ? 'Connection Test Passed' : 'Connection Test Failed',
message: dialogContent, message: dialogContent,
@ -487,37 +487,37 @@ window.app = Vue.createApp({
label: 'Close' label: 'Close'
} }
}) })
// Also show a brief notification // Also show a brief notification
this.$q.notify({ this.$q.notify({
type: data.success ? 'positive' : 'negative', type: data.success ? 'positive' : 'negative',
message: data.message, message: data.message,
timeout: 3000 timeout: 3000
}) })
} catch (error) { } catch (error) {
LNbits.utils.notifyApiError(error) LNbits.utils.notifyApiError(error)
} finally { } finally {
this.testingConnection = false this.testingConnection = false
} }
}, },
async manualPoll() { async manualPoll() {
this.runningManualPoll = true this.runningManualPoll = true
try { try {
const {data} = await LNbits.api.request( const { data } = await LNbits.api.request(
'POST', 'POST',
'/satmachineadmin/api/v1/dca/manual-poll', '/satmachineadmin/api/v1/dca/manual-poll',
this.g.user.wallets[0].adminkey null
) )
this.lastPollTime = new Date().toLocaleString() this.lastPollTime = new Date().toLocaleString()
this.$q.notify({ this.$q.notify({
type: 'positive', type: 'positive',
message: `Manual poll completed. Found ${data.transactions_processed} new transactions.`, message: `Manual poll completed. Found ${data.transactions_processed} new transactions.`,
timeout: 5000 timeout: 5000
}) })
// Refresh data // Refresh data
await this.getDcaClients() // Refresh to show updated balances await this.getDcaClients() // Refresh to show updated balances
await this.getDeposits() await this.getDeposits()
@ -529,19 +529,19 @@ window.app = Vue.createApp({
this.runningManualPoll = false this.runningManualPoll = false
} }
}, },
async testTransaction() { async testTransaction() {
this.runningTestTransaction = true this.runningTestTransaction = true
try { try {
const {data} = await LNbits.api.request( const { data } = await LNbits.api.request(
'POST', 'POST',
'/satmachineadmin/api/v1/dca/test-transaction', '/satmachineadmin/api/v1/dca/test-transaction',
this.g.user.wallets[0].adminkey null
) )
// Show detailed results in a dialog // Show detailed results in a dialog
const details = data.transaction_details const details = data.transaction_details
let dialogContent = `<strong>Test Transaction Results</strong><br/><br/>` let dialogContent = `<strong>Test Transaction Results</strong><br/><br/>`
dialogContent += `<strong>Transaction ID:</strong> ${details.transaction_id}<br/>` dialogContent += `<strong>Transaction ID:</strong> ${details.transaction_id}<br/>`
dialogContent += `<strong>Total Amount:</strong> ${details.total_amount_sats} sats<br/>` dialogContent += `<strong>Total Amount:</strong> ${details.total_amount_sats} sats<br/>`
@ -552,7 +552,7 @@ window.app = Vue.createApp({
dialogContent += `<strong>Effective Commission:</strong> ${details.effective_commission}%<br/>` dialogContent += `<strong>Effective Commission:</strong> ${details.effective_commission}%<br/>`
} }
dialogContent += `<br/><strong>Check your wallets to see the distributions!</strong>` dialogContent += `<br/><strong>Check your wallets to see the distributions!</strong>`
this.$q.dialog({ this.$q.dialog({
title: 'Test Transaction Completed', title: 'Test Transaction Completed',
message: dialogContent, message: dialogContent,
@ -562,20 +562,20 @@ window.app = Vue.createApp({
label: 'Great!' label: 'Great!'
} }
}) })
// Also show a brief notification // Also show a brief notification
this.$q.notify({ this.$q.notify({
type: 'positive', type: 'positive',
message: `Test transaction processed: ${details.total_amount_sats} sats distributed`, message: `Test transaction processed: ${details.total_amount_sats} sats distributed`,
timeout: 5000 timeout: 5000
}) })
// Refresh data // Refresh data
await this.getDcaClients() // Refresh to show updated balances await this.getDcaClients() // Refresh to show updated balances
await this.getDeposits() await this.getDeposits()
await this.getLamassuTransactions() await this.getLamassuTransactions()
await this.getLamassuConfig() await this.getLamassuConfig()
} catch (error) { } catch (error) {
LNbits.utils.notifyApiError(error) LNbits.utils.notifyApiError(error)
} finally { } finally {
@ -589,7 +589,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].adminkey null
) )
this.lamassuTransactions = data this.lamassuTransactions = data
} catch (error) { } catch (error) {
@ -602,9 +602,9 @@ 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].adminkey null
) )
this.distributionDialog.transaction = transaction this.distributionDialog.transaction = transaction
this.distributionDialog.distributions = distributions this.distributionDialog.distributions = distributions
this.distributionDialog.show = true this.distributionDialog.show = true
@ -630,20 +630,20 @@ window.app = Vue.createApp({
computed: { computed: {
isConfigFormValid() { isConfigFormValid() {
const data = this.configDialog.data const data = this.configDialog.data
// Basic database fields are required // Basic database fields are required
const basicValid = data.host && data.database_name && data.username && data.selectedWallet const basicValid = data.host && data.database_name && data.username && data.selectedWallet
// If SSH tunnel is enabled, validate SSH fields // If SSH tunnel is enabled, validate SSH fields
if (data.use_ssh_tunnel) { if (data.use_ssh_tunnel) {
const sshValid = data.ssh_host && data.ssh_username && const sshValid = data.ssh_host && data.ssh_username &&
(data.ssh_password || data.ssh_private_key) (data.ssh_password || data.ssh_private_key)
return basicValid && sshValid return basicValid && sshValid
} }
return basicValid return basicValid
}, },
clientOptions() { clientOptions() {
return this.dcaClients.map(client => ({ return this.dcaClients.map(client => ({
label: `${client.username || client.user_id.substring(0, 8) + '...'} (${client.dca_mode})`, label: `${client.username || client.user_id.substring(0, 8) + '...'} (${client.dca_mode})`,

View file

@ -3,7 +3,7 @@
from fastapi import APIRouter, Depends, Request from fastapi import APIRouter, Depends, Request
from fastapi.responses import HTMLResponse from fastapi.responses import HTMLResponse
from lnbits.core.models import User from lnbits.core.models import User
from lnbits.decorators import check_user_exists from lnbits.decorators import check_super_user
from lnbits.helpers import template_renderer from lnbits.helpers import template_renderer
satmachineadmin_generic_router = APIRouter() satmachineadmin_generic_router = APIRouter()
@ -13,9 +13,9 @@ def satmachineadmin_renderer():
return template_renderer(["satmachineadmin/templates"]) return template_renderer(["satmachineadmin/templates"])
# DCA Admin page # DCA Admin page - Requires superuser access
@satmachineadmin_generic_router.get("/", response_class=HTMLResponse) @satmachineadmin_generic_router.get("/", response_class=HTMLResponse)
async def index(req: Request, user: User = Depends(check_user_exists)): async def index(req: Request, user: User = Depends(check_super_user)):
return satmachineadmin_renderer().TemplateResponse( return satmachineadmin_renderer().TemplateResponse(
"satmachineadmin/index.html", {"request": req, "user": user.json()} "satmachineadmin/index.html", {"request": req, "user": user.json()}
) )

View file

@ -5,9 +5,9 @@ from typing import Optional
from fastapi import APIRouter, Depends, Request 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 User, WalletTypeInfo
from lnbits.core.services import create_invoice from lnbits.core.services import create_invoice
from lnbits.decorators import require_admin_key from lnbits.decorators import check_super_user
from starlette.exceptions import HTTPException from starlette.exceptions import HTTPException
from .crud import ( from .crud import (
@ -59,7 +59,7 @@ satmachineadmin_api_router = APIRouter()
@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_admin_key), wallet: WalletTypeInfo = Depends(check_super_user),
) -> list[DcaClient]: ) -> list[DcaClient]:
"""Get all DCA clients""" """Get all DCA clients"""
return await get_dca_clients() return await get_dca_clients()
@ -68,7 +68,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_admin_key), wallet: WalletTypeInfo = Depends(check_super_user),
) -> 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)
@ -83,12 +83,10 @@ async def api_get_dca_client(
# Admin extension only reads existing clients and manages their deposits # Admin extension only reads existing clients and manages their deposits
@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_admin_key), wallet: WalletTypeInfo = Depends(check_super_user),
) -> ClientBalanceSummary: ) -> ClientBalanceSummary:
"""Get client balance summary""" """Get client balance summary"""
client = await get_dca_client(client_id) client = await get_dca_client(client_id)
@ -105,7 +103,7 @@ async def api_get_client_balance(
@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_admin_key), wallet: WalletTypeInfo = Depends(check_super_user),
) -> list[DcaDeposit]: ) -> list[DcaDeposit]:
"""Get all deposits""" """Get all deposits"""
return await get_all_deposits() return await get_all_deposits()
@ -114,7 +112,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_admin_key), wallet: WalletTypeInfo = Depends(check_super_user),
) -> DcaDeposit: ) -> DcaDeposit:
"""Get a specific deposit""" """Get a specific deposit"""
deposit = await get_deposit(deposit_id) deposit = await get_deposit(deposit_id)
@ -128,7 +126,7 @@ async def api_get_deposit(
@satmachineadmin_api_router.post("/api/v1/dca/deposits", status_code=HTTPStatus.CREATED) @satmachineadmin_api_router.post("/api/v1/dca/deposits", status_code=HTTPStatus.CREATED)
async def api_create_deposit( async def api_create_deposit(
data: CreateDepositData, data: CreateDepositData,
wallet: WalletTypeInfo = Depends(require_admin_key), user: User = Depends(check_super_user),
) -> DcaDeposit: ) -> DcaDeposit:
"""Create a new deposit""" """Create a new deposit"""
# Verify client exists # Verify client exists
@ -145,7 +143,7 @@ async def api_create_deposit(
async def api_update_deposit_status( async def api_update_deposit_status(
deposit_id: str, deposit_id: str,
data: UpdateDepositStatusData, data: UpdateDepositStatusData,
wallet: WalletTypeInfo = Depends(require_admin_key), user: User = Depends(check_super_user),
) -> DcaDeposit: ) -> DcaDeposit:
"""Update deposit status (e.g., confirm deposit)""" """Update deposit status (e.g., confirm deposit)"""
deposit = await get_deposit(deposit_id) deposit = await get_deposit(deposit_id)
@ -168,7 +166,7 @@ async def api_update_deposit_status(
@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), user: User = Depends(check_super_user),
): ):
"""Test connection to Lamassu database with detailed reporting""" """Test connection to Lamassu database with detailed reporting"""
try: try:
@ -191,7 +189,7 @@ async def api_test_database_connection(
@satmachineadmin_api_router.post("/api/v1/dca/manual-poll") @satmachineadmin_api_router.post("/api/v1/dca/manual-poll")
async def api_manual_poll( async def api_manual_poll(
wallet: WalletTypeInfo = Depends(require_admin_key), user: User = Depends(check_super_user),
): ):
"""Manually trigger a poll of the Lamassu database""" """Manually trigger a poll of the Lamassu database"""
try: try:
@ -237,7 +235,7 @@ async def api_manual_poll(
@satmachineadmin_api_router.post("/api/v1/dca/test-transaction") @satmachineadmin_api_router.post("/api/v1/dca/test-transaction")
async def api_test_transaction( async def api_test_transaction(
wallet: WalletTypeInfo = Depends(require_admin_key), user: User = Depends(check_super_user),
crypto_atoms: int = 103, crypto_atoms: int = 103,
commission_percentage: float = 0.03, commission_percentage: float = 0.03,
discount: float = 0.0, discount: float = 0.0,
@ -303,7 +301,7 @@ async def api_test_transaction(
@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_admin_key), wallet: WalletTypeInfo = Depends(check_super_user),
) -> 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()
@ -312,7 +310,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_admin_key), wallet: WalletTypeInfo = Depends(check_super_user),
) -> 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)
@ -328,7 +326,7 @@ async def api_get_lamassu_transaction(
) )
async def api_get_transaction_distributions( async def api_get_transaction_distributions(
transaction_id: str, transaction_id: str,
wallet: WalletTypeInfo = Depends(require_admin_key), wallet: WalletTypeInfo = Depends(check_super_user),
) -> 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
@ -371,7 +369,7 @@ async def api_get_transaction_distributions(
@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_admin_key), wallet: WalletTypeInfo = Depends(check_super_user),
) -> 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()
@ -380,7 +378,7 @@ async def api_get_lamassu_config(
@satmachineadmin_api_router.post("/api/v1/dca/config", status_code=HTTPStatus.CREATED) @satmachineadmin_api_router.post("/api/v1/dca/config", status_code=HTTPStatus.CREATED)
async def api_create_lamassu_config( async def api_create_lamassu_config(
data: CreateLamassuConfigData, data: CreateLamassuConfigData,
wallet: WalletTypeInfo = Depends(require_admin_key), user: User = Depends(check_super_user),
) -> LamassuConfig: ) -> LamassuConfig:
"""Create/update Lamassu database configuration""" """Create/update Lamassu database configuration"""
return await create_lamassu_config(data) return await create_lamassu_config(data)
@ -390,7 +388,7 @@ async def api_create_lamassu_config(
async def api_update_lamassu_config( async def api_update_lamassu_config(
config_id: str, config_id: str,
data: UpdateLamassuConfigData, data: UpdateLamassuConfigData,
wallet: WalletTypeInfo = Depends(require_admin_key), user: User = Depends(check_super_user),
) -> LamassuConfig: ) -> LamassuConfig:
"""Update Lamassu database configuration""" """Update Lamassu database configuration"""
config = await get_lamassu_config(config_id) config = await get_lamassu_config(config_id)
@ -411,7 +409,7 @@ async def api_update_lamassu_config(
@satmachineadmin_api_router.delete("/api/v1/dca/config/{config_id}") @satmachineadmin_api_router.delete("/api/v1/dca/config/{config_id}")
async def api_delete_lamassu_config( async def api_delete_lamassu_config(
config_id: str, config_id: str,
wallet: WalletTypeInfo = Depends(require_admin_key), user: User = Depends(check_super_user),
): ):
"""Delete Lamassu database configuration""" """Delete Lamassu database configuration"""
config = await get_lamassu_config(config_id) config = await get_lamassu_config(config_id)