Adds super user and config setup
Adds super user role to restrict settings changes. Improves the settings screen to only allow super users to make modifications. Adds a warning banner if the Castle wallet is not configured. Changes admin key to inkey for fetching settings. This fixes an issue where settings weren't accessible. Adds a validation to require the Castle wallet ID when updating settings.
This commit is contained in:
parent
29983cedb7
commit
31344607c6
3 changed files with 106 additions and 16 deletions
|
|
@ -13,6 +13,8 @@ window.app = Vue.createApp({
|
||||||
currencies: [],
|
currencies: [],
|
||||||
settings: null,
|
settings: null,
|
||||||
isAdmin: false,
|
isAdmin: false,
|
||||||
|
isSuperUser: false,
|
||||||
|
castleWalletConfigured: false,
|
||||||
expenseDialog: {
|
expenseDialog: {
|
||||||
show: false,
|
show: false,
|
||||||
description: '',
|
description: '',
|
||||||
|
|
@ -104,16 +106,21 @@ window.app = Vue.createApp({
|
||||||
},
|
},
|
||||||
async loadSettings() {
|
async loadSettings() {
|
||||||
try {
|
try {
|
||||||
|
// Try with admin key first to check settings
|
||||||
const response = await LNbits.api.request(
|
const response = await LNbits.api.request(
|
||||||
'GET',
|
'GET',
|
||||||
'/castle/api/v1/settings',
|
'/castle/api/v1/settings',
|
||||||
this.g.user.wallets[0].adminkey
|
this.g.user.wallets[0].inkey
|
||||||
)
|
)
|
||||||
this.settings = response.data
|
this.settings = response.data
|
||||||
this.isAdmin = true
|
this.castleWalletConfigured = !!(this.settings && this.settings.castle_wallet_id)
|
||||||
|
|
||||||
|
// Check if user is super user by seeing if they can access admin features
|
||||||
|
this.isSuperUser = this.g.user.super_user || false
|
||||||
|
this.isAdmin = this.g.user.admin || this.isSuperUser
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Not admin or settings not available
|
// Settings not available
|
||||||
this.isAdmin = false
|
this.castleWalletConfigured = false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
showSettingsDialog() {
|
showSettingsDialog() {
|
||||||
|
|
@ -121,6 +128,14 @@ window.app = Vue.createApp({
|
||||||
this.settingsDialog.show = true
|
this.settingsDialog.show = true
|
||||||
},
|
},
|
||||||
async submitSettings() {
|
async submitSettings() {
|
||||||
|
if (!this.settingsDialog.castleWalletId) {
|
||||||
|
this.$q.notify({
|
||||||
|
type: 'warning',
|
||||||
|
message: 'Castle Wallet ID is required'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
this.settingsDialog.loading = true
|
this.settingsDialog.loading = true
|
||||||
try {
|
try {
|
||||||
await LNbits.api.request(
|
await LNbits.api.request(
|
||||||
|
|
|
||||||
|
|
@ -16,15 +16,37 @@
|
||||||
<h5 class="q-my-none">🏰 Castle Accounting</h5>
|
<h5 class="q-my-none">🏰 Castle Accounting</h5>
|
||||||
<p class="q-mb-none">Track expenses, receivables, and balances for the collective</p>
|
<p class="q-mb-none">Track expenses, receivables, and balances for the collective</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-auto" v-if="isAdmin">
|
<div class="col-auto" v-if="isSuperUser">
|
||||||
<q-btn flat round icon="settings" @click="showSettingsDialog">
|
<q-btn flat round icon="settings" @click="showSettingsDialog">
|
||||||
<q-tooltip>Settings</q-tooltip>
|
<q-tooltip>Settings (Super User Only)</q-tooltip>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
</q-card>
|
</q-card>
|
||||||
|
|
||||||
|
<!-- Setup Warning -->
|
||||||
|
<q-banner v-if="!castleWalletConfigured && isSuperUser" class="bg-warning text-white" rounded>
|
||||||
|
<template v-slot:avatar>
|
||||||
|
<q-icon name="warning" color="white"></q-icon>
|
||||||
|
</template>
|
||||||
|
<div>
|
||||||
|
<strong>Setup Required:</strong> Castle Wallet ID must be configured before the extension can function.
|
||||||
|
</div>
|
||||||
|
<template v-slot:action>
|
||||||
|
<q-btn flat color="white" label="Configure Now" @click="showSettingsDialog"></q-btn>
|
||||||
|
</template>
|
||||||
|
</q-banner>
|
||||||
|
|
||||||
|
<q-banner v-if="!castleWalletConfigured && !isSuperUser" class="bg-info text-white" rounded>
|
||||||
|
<template v-slot:avatar>
|
||||||
|
<q-icon name="info" color="white"></q-icon>
|
||||||
|
</template>
|
||||||
|
<div>
|
||||||
|
<strong>Setup Required:</strong> This extension requires configuration by the super user before it can be used.
|
||||||
|
</div>
|
||||||
|
</q-banner>
|
||||||
|
|
||||||
<!-- User Balance Card -->
|
<!-- User Balance Card -->
|
||||||
<q-card>
|
<q-card>
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
|
|
@ -66,8 +88,15 @@
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
<h6 class="q-my-none q-mb-md">Quick Actions</h6>
|
<h6 class="q-my-none q-mb-md">Quick Actions</h6>
|
||||||
<div class="row q-gutter-sm">
|
<div class="row q-gutter-sm">
|
||||||
<q-btn color="primary" @click="expenseDialog.show = true">
|
<q-btn
|
||||||
|
color="primary"
|
||||||
|
@click="expenseDialog.show = true"
|
||||||
|
:disable="!castleWalletConfigured"
|
||||||
|
>
|
||||||
Add Expense
|
Add Expense
|
||||||
|
<q-tooltip v-if="!castleWalletConfigured">
|
||||||
|
Castle wallet must be configured first
|
||||||
|
</q-tooltip>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
<q-btn color="secondary" @click="loadTransactions">
|
<q-btn color="secondary" @click="loadTransactions">
|
||||||
View Transactions
|
View Transactions
|
||||||
|
|
@ -248,12 +277,22 @@
|
||||||
<q-form @submit="submitSettings" class="q-gutter-md">
|
<q-form @submit="submitSettings" class="q-gutter-md">
|
||||||
<div class="text-h6 q-mb-md">Castle Settings</div>
|
<div class="text-h6 q-mb-md">Castle Settings</div>
|
||||||
|
|
||||||
|
<q-banner v-if="!isSuperUser" class="bg-warning text-dark q-mb-md" dense rounded>
|
||||||
|
<template v-slot:avatar>
|
||||||
|
<q-icon name="lock" color="orange"></q-icon>
|
||||||
|
</template>
|
||||||
|
<div class="text-caption">
|
||||||
|
<strong>Super User Only:</strong> Only the super user can modify these settings.
|
||||||
|
</div>
|
||||||
|
</q-banner>
|
||||||
|
|
||||||
<q-input
|
<q-input
|
||||||
filled
|
filled
|
||||||
dense
|
dense
|
||||||
v-model.trim="settingsDialog.castleWalletId"
|
v-model.trim="settingsDialog.castleWalletId"
|
||||||
label="Castle Wallet ID *"
|
label="Castle Wallet ID *"
|
||||||
placeholder="The wallet ID that represents the Castle"
|
placeholder="The wallet ID that represents the Castle"
|
||||||
|
:readonly="!isSuperUser"
|
||||||
></q-input>
|
></q-input>
|
||||||
|
|
||||||
<div class="text-caption text-grey">
|
<div class="text-caption text-grey">
|
||||||
|
|
@ -261,10 +300,18 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row q-mt-lg">
|
<div class="row q-mt-lg">
|
||||||
<q-btn unelevated color="primary" type="submit" :loading="settingsDialog.loading">
|
<q-btn
|
||||||
|
v-if="isSuperUser"
|
||||||
|
unelevated
|
||||||
|
color="primary"
|
||||||
|
type="submit"
|
||||||
|
:loading="settingsDialog.loading"
|
||||||
|
>
|
||||||
Save Settings
|
Save Settings
|
||||||
</q-btn>
|
</q-btn>
|
||||||
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Cancel</q-btn>
|
<q-btn v-close-popup flat color="grey" :class="isSuperUser ? 'q-ml-auto' : ''">
|
||||||
|
{% raw %}{{ isSuperUser ? 'Cancel' : 'Close' }}{% endraw %}
|
||||||
|
</q-btn>
|
||||||
</div>
|
</div>
|
||||||
</q-form>
|
</q-form>
|
||||||
</q-card>
|
</q-card>
|
||||||
|
|
|
||||||
42
views_api.py
42
views_api.py
|
|
@ -1,8 +1,13 @@
|
||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, HTTPException
|
from fastapi import APIRouter, Depends, HTTPException
|
||||||
from lnbits.core.models import WalletTypeInfo
|
from lnbits.core.models import User, WalletTypeInfo
|
||||||
from lnbits.decorators import require_admin_key, require_invoice_key
|
from lnbits.decorators import (
|
||||||
|
check_super_user,
|
||||||
|
check_user_exists,
|
||||||
|
require_admin_key,
|
||||||
|
require_invoice_key,
|
||||||
|
)
|
||||||
from lnbits.utils.exchange_rates import allowed_currencies, fiat_amount_as_satoshis
|
from lnbits.utils.exchange_rates import allowed_currencies, fiat_amount_as_satoshis
|
||||||
|
|
||||||
from .crud import (
|
from .crud import (
|
||||||
|
|
@ -37,6 +42,20 @@ from .services import get_settings, update_settings
|
||||||
castle_api_router = APIRouter()
|
castle_api_router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
# ===== HELPER FUNCTIONS =====
|
||||||
|
|
||||||
|
|
||||||
|
async def check_castle_wallet_configured() -> str:
|
||||||
|
"""Ensure castle wallet is configured, return wallet_id"""
|
||||||
|
settings = await get_settings("admin")
|
||||||
|
if not settings or not settings.castle_wallet_id:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=HTTPStatus.BAD_REQUEST,
|
||||||
|
detail="Castle wallet not configured. Please contact the super user to configure the Castle wallet in settings.",
|
||||||
|
)
|
||||||
|
return settings.castle_wallet_id
|
||||||
|
|
||||||
|
|
||||||
# ===== UTILITY ENDPOINTS =====
|
# ===== UTILITY ENDPOINTS =====
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -459,18 +478,27 @@ async def api_pay_user(
|
||||||
|
|
||||||
@castle_api_router.get("/api/v1/settings")
|
@castle_api_router.get("/api/v1/settings")
|
||||||
async def api_get_settings(
|
async def api_get_settings(
|
||||||
wallet: WalletTypeInfo = Depends(require_admin_key),
|
user: User = Depends(check_user_exists),
|
||||||
) -> CastleSettings:
|
) -> CastleSettings:
|
||||||
"""Get Castle settings (admin only)"""
|
"""Get Castle settings"""
|
||||||
user_id = "admin"
|
user_id = "admin"
|
||||||
return await get_settings(user_id)
|
settings = await get_settings(user_id)
|
||||||
|
# Return empty settings if not configured (so UI can show setup screen)
|
||||||
|
if not settings:
|
||||||
|
return CastleSettings()
|
||||||
|
return settings
|
||||||
|
|
||||||
|
|
||||||
@castle_api_router.put("/api/v1/settings")
|
@castle_api_router.put("/api/v1/settings")
|
||||||
async def api_update_settings(
|
async def api_update_settings(
|
||||||
data: CastleSettings,
|
data: CastleSettings,
|
||||||
wallet: WalletTypeInfo = Depends(require_admin_key),
|
user: User = Depends(check_super_user),
|
||||||
) -> CastleSettings:
|
) -> CastleSettings:
|
||||||
"""Update Castle settings (admin only)"""
|
"""Update Castle settings (super user only)"""
|
||||||
|
if not data.castle_wallet_id:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=HTTPStatus.BAD_REQUEST,
|
||||||
|
detail="Castle wallet ID is required",
|
||||||
|
)
|
||||||
user_id = "admin"
|
user_id = "admin"
|
||||||
return await update_settings(user_id, data)
|
return await update_settings(user_id, data)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue