Enables user selection for permissions
Replaces the user ID input field with a user selection dropdown, allowing administrators to search and select users for permission management. This simplifies the process of assigning permissions and improves user experience. Fetches Castle users via a new API endpoint and filters them based on search input. Only users with Castle accounts (receivables, payables, equity, or permissions) are listed.
This commit is contained in:
parent
fc12dae435
commit
d6a1c6e5b3
3 changed files with 142 additions and 7 deletions
|
|
@ -5,6 +5,8 @@ window.app = Vue.createApp({
|
|||
return {
|
||||
permissions: [],
|
||||
accounts: [],
|
||||
users: [],
|
||||
filteredUsers: [],
|
||||
loading: false,
|
||||
granting: false,
|
||||
revoking: false,
|
||||
|
|
@ -48,6 +50,15 @@ window.app = Vue.createApp({
|
|||
}))
|
||||
},
|
||||
|
||||
userOptions() {
|
||||
const users = this.filteredUsers.length > 0 ? this.filteredUsers : this.users
|
||||
return users.map(user => ({
|
||||
id: user.id,
|
||||
username: user.username || user.id,
|
||||
label: user.username ? `${user.username} (${user.id.substring(0, 8)}...)` : user.id
|
||||
}))
|
||||
},
|
||||
|
||||
isGrantFormValid() {
|
||||
return !!(
|
||||
this.grantForm.user_id &&
|
||||
|
|
@ -130,6 +141,48 @@ window.app = Vue.createApp({
|
|||
}
|
||||
},
|
||||
|
||||
async loadUsers() {
|
||||
if (!this.isSuperUser) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await LNbits.api.request(
|
||||
'GET',
|
||||
'/castle/api/v1/admin/castle-users',
|
||||
this.g.user.wallets[0].adminkey
|
||||
)
|
||||
this.users = response.data || []
|
||||
this.filteredUsers = []
|
||||
} catch (error) {
|
||||
console.error('Failed to load users:', error)
|
||||
this.$q.notify({
|
||||
type: 'negative',
|
||||
message: 'Failed to load users',
|
||||
caption: error.message || 'Unknown error',
|
||||
timeout: 5000
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
filterUsers(val, update) {
|
||||
if (val === '') {
|
||||
update(() => {
|
||||
this.filteredUsers = []
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
update(() => {
|
||||
const needle = val.toLowerCase()
|
||||
this.filteredUsers = this.users.filter(user => {
|
||||
const username = user.username || ''
|
||||
const userId = user.id || ''
|
||||
return username.toLowerCase().includes(needle) || userId.toLowerCase().includes(needle)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
async grantPermission() {
|
||||
if (!this.isGrantFormValid) {
|
||||
this.$q.notify({
|
||||
|
|
@ -283,7 +336,10 @@ window.app = Vue.createApp({
|
|||
if (this.g.user.wallets && this.g.user.wallets.length > 0) {
|
||||
await this.loadAccounts()
|
||||
if (this.isSuperUser) {
|
||||
await this.loadPermissions()
|
||||
await Promise.all([
|
||||
this.loadPermissions(),
|
||||
this.loadUsers()
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -187,19 +187,33 @@
|
|||
</q-card-section>
|
||||
|
||||
<q-card-section class="q-gutter-md">
|
||||
<!-- User ID -->
|
||||
<q-input
|
||||
<!-- User -->
|
||||
<q-select
|
||||
v-model="grantForm.user_id"
|
||||
label="User ID *"
|
||||
hint="Wallet ID of the user"
|
||||
label="User *"
|
||||
hint="Search and select a user"
|
||||
:options="userOptions"
|
||||
option-value="id"
|
||||
option-label="label"
|
||||
emit-value
|
||||
map-options
|
||||
use-input
|
||||
@filter="filterUsers"
|
||||
outlined
|
||||
dense
|
||||
:rules="[val => !!val || 'User ID is required']"
|
||||
:rules="[val => !!val || 'User is required']"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon name="person"></q-icon>
|
||||
</template>
|
||||
</q-input>
|
||||
<template v-slot:no-option>
|
||||
<q-item>
|
||||
<q-item-section class="text-grey">
|
||||
No users found
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</template>
|
||||
</q-select>
|
||||
|
||||
<!-- Account -->
|
||||
<q-select
|
||||
|
|
|
|||
65
views_api.py
65
views_api.py
|
|
@ -1339,6 +1339,71 @@ async def api_get_all_users(
|
|||
return users
|
||||
|
||||
|
||||
@castle_api_router.get("/api/v1/admin/castle-users")
|
||||
async def api_get_castle_users(
|
||||
wallet: WalletTypeInfo = Depends(require_admin_key),
|
||||
) -> list[dict]:
|
||||
"""
|
||||
Get all users who have Castle accounts (receivables, payables, equity, or permissions).
|
||||
This returns only users who are actively using Castle, not all LNbits users.
|
||||
Admin only.
|
||||
"""
|
||||
from lnbits.core.crud.users import get_user
|
||||
from .crud import get_all_equity_eligible_users
|
||||
|
||||
# Get all user-specific accounts (Receivable/Payable/Equity)
|
||||
all_accounts = await get_all_accounts()
|
||||
user_accounts = [acc for acc in all_accounts if acc.user_id is not None]
|
||||
|
||||
# Get all users who have permissions
|
||||
all_permissions = []
|
||||
for account in all_accounts:
|
||||
account_perms = await get_account_permissions(account.id)
|
||||
all_permissions.extend(account_perms)
|
||||
|
||||
# Get all equity-eligible users
|
||||
equity_users = await get_all_equity_eligible_users()
|
||||
|
||||
# Collect unique user IDs
|
||||
user_ids = set()
|
||||
|
||||
# Add users with accounts
|
||||
for acc in user_accounts:
|
||||
user_ids.add(acc.user_id)
|
||||
|
||||
# Add users with permissions
|
||||
for perm in all_permissions:
|
||||
user_ids.add(perm.user_id)
|
||||
|
||||
# Add equity-eligible users
|
||||
for equity in equity_users:
|
||||
user_ids.add(equity.user_id)
|
||||
|
||||
# Build user list with enriched data
|
||||
users = []
|
||||
for user_id in user_ids:
|
||||
# Get user details from core
|
||||
user = await get_user(user_id)
|
||||
|
||||
# Use username if available, otherwise use user_id
|
||||
username = user.username if user and user.username else None
|
||||
|
||||
# Get user's wallet setting if exists
|
||||
user_wallet = await get_user_wallet(user_id)
|
||||
|
||||
users.append({
|
||||
"id": user_id,
|
||||
"user_id": user_id, # Compatibility with existing code
|
||||
"username": username,
|
||||
"user_wallet_id": user_wallet.user_wallet_id if user_wallet else None,
|
||||
})
|
||||
|
||||
# Sort by username (None values last)
|
||||
users.sort(key=lambda x: (x["username"] is None, x["username"] or "", x["user_id"]))
|
||||
|
||||
return users
|
||||
|
||||
|
||||
@castle_api_router.get("/api/v1/user/wallet")
|
||||
async def api_get_user_wallet(
|
||||
user: User = Depends(check_user_exists),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue