feat: usermanager (#2139)

* feat: usermanager

---------

Co-authored-by: Vlad Stan <stan.v.vlad@gmail.com>
This commit is contained in:
dni ⚡
2024-05-10 12:06:46 +02:00
committed by GitHub
co-authored by Vlad Stan
parent eae5002b69
commit 9ca14f200d
19 changed files with 961 additions and 144 deletions
+1 -1
View File
File diff suppressed because one or more lines are too long
+4 -1
View File
@@ -9,6 +9,8 @@ window.localisation.en = {
transactions: 'Transactions',
dashboard: 'Dashboard',
node: 'Node',
export_users: 'Export Users',
no_users: 'No users found',
total_capacity: 'Total Capacity',
avg_channel_size: 'Avg. Channel Size',
biggest_channel_size: 'Biggest Channel Size',
@@ -245,5 +247,6 @@ window.localisation.en = {
pay_from_wallet: 'Pay from Wallet',
show_qr: 'Show QR',
retry_install: 'Retry Install',
new_payment: 'Make New Payment'
new_payment: 'Make New Payment',
hide_empty_wallets: 'Hide empty wallets'
}
-50
View File
@@ -56,9 +56,6 @@ new Vue({
'yellow',
'orange'
],
topUpDialog: {
show: false
},
tab: 'funding',
needsRestart: false
}
@@ -80,31 +77,6 @@ new Vue({
}
},
methods: {
addAdminUser() {
let addUser = this.formAddAdmin
let admin_users = this.formData.lnbits_admin_users
if (addUser && addUser.length && !admin_users.includes(addUser)) {
//admin_users = [...admin_users, addUser]
this.formData.lnbits_admin_users = [...admin_users, addUser]
this.formAddAdmin = ''
}
},
removeAdminUser(user) {
let admin_users = this.formData.lnbits_admin_users
this.formData.lnbits_admin_users = admin_users.filter(u => u !== user)
},
addAllowedUser() {
let addUser = this.formAddUser
let allowed_users = this.formData.lnbits_allowed_users
if (addUser && addUser.length && !allowed_users.includes(addUser)) {
this.formData.lnbits_allowed_users = [...allowed_users, addUser]
this.formAddUser = ''
}
},
removeAllowedUser(user) {
let allowed_users = this.formData.lnbits_allowed_users
this.formData.lnbits_allowed_users = allowed_users.filter(u => u !== user)
},
addExtensionsManifest() {
const addManifest = this.formAddExtensionsManifest.trim()
const manifests = this.formData.lnbits_extensions_manifests
@@ -200,28 +172,6 @@ new Vue({
LNbits.utils.notifyApiError(error)
})
},
topupWallet() {
LNbits.api
.request(
'PUT',
'/admin/api/v1/topup/',
this.g.user.wallets[0].adminkey,
this.wallet
)
.then(response => {
this.$q.notify({
type: 'positive',
message: this.$t('wallet_topup_ok', {
amount: this.wallet.amount
}),
icon: null
})
this.wallet = {}
})
.catch(function (error) {
LNbits.utils.notifyApiError(error)
})
},
formatDate(date) {
return moment(date * 1000).fromNow()
},
+1 -1
View File
@@ -145,7 +145,7 @@ window.LNbits = {
)
},
updateBalance: function (credit, wallet_id) {
return LNbits.api.request('PUT', '/admin/api/v1/topup/', null, {
return LNbits.api.request('PUT', '/users/api/v1/topup/', null, {
amount: credit,
id: wallet_id
})
+9 -1
View File
@@ -170,7 +170,7 @@ Vue.component('lnbits-extension-list', {
})
Vue.component('lnbits-manage', {
props: ['showAdmin', 'showNode', 'showExtensions'],
props: ['showAdmin', 'showNode', 'showExtensions', 'showUsers'],
methods: {
isActive: function (path) {
return window.location.pathname === path
@@ -211,6 +211,14 @@ Vue.component('lnbits-manage', {
<q-item-label lines="1" v-text="$t('extensions')"></q-item-label>
</q-item-section>
</q-item>
<q-item v-if="showUsers" clickable tag="a" href="/users" :active="isActive('/users')">
<q-item-section side>
<q-icon name="groups" :color="isActive('/users') ? 'primary' : 'grey-5'" size="md"></q-icon>
</q-item-section>
<q-item-section>
<q-item-label lines="1" v-text="$t('users')"></q-item-label>
</q-item-section>
</q-item>
</q-list>
`,
+374
View File
@@ -0,0 +1,374 @@
new Vue({
el: '#vue',
mixins: [windowMixin],
data: function () {
return {
isSuperUser: false,
wallet: {},
cancel: {},
users: [],
wallets: [],
walletDialog: {
show: false
},
topupDialog: {
show: false
},
createUserDialog: {
data: {},
fields: [
{
description: 'Username',
name: 'username'
},
{
description: 'Email',
name: 'email'
},
{
type: 'password',
description: 'Password',
name: 'password'
}
],
show: false
},
createWalletDialog: {
data: {},
fields: [
{
type: 'str',
description: 'Wallet Name',
name: 'name'
},
{
type: 'select',
values: ['', 'EUR', 'USD'],
description: 'Currency',
name: 'currency'
},
{
type: 'str',
description: 'Balance',
name: 'balance'
}
],
show: false
},
walletTable: {
columns: [
{
name: 'name',
align: 'left',
label: 'Name',
field: 'name'
},
{
name: 'currency',
align: 'left',
label: 'Currency',
field: 'currency'
},
{
name: 'balance_msat',
align: 'left',
label: 'Balance',
field: 'balance_msat'
},
{
name: 'deleted',
align: 'left',
label: 'Deleted',
field: 'deleted'
}
]
},
usersTable: {
columns: [
{
name: 'balance_msat',
align: 'left',
label: 'Balance',
field: 'balance_msat',
sortable: true
},
{
name: 'wallet_count',
align: 'left',
label: 'Wallet Count',
field: 'wallet_count',
sortable: true
},
{
name: 'transaction_count',
align: 'left',
label: 'Transaction Count',
field: 'transaction_count',
sortable: true
},
{
name: 'username',
align: 'left',
label: 'Username',
field: 'username',
sortable: true
},
{
name: 'email',
align: 'left',
label: 'Email',
field: 'email',
sortable: true
},
{
name: 'last_payment',
align: 'left',
label: 'Last Payment',
field: 'last_payment',
sortable: true
}
],
pagination: {
sortBy: 'balance_msat',
rowsPerPage: 10,
page: 1,
descending: true,
rowsNumber: 10
},
search: null,
hideEmpty: true,
loading: false
}
}
},
watch: {
'usersTable.hideEmpty': function (newVal, _) {
if (newVal) {
this.usersTable.filter = {
'transaction_count[gt]': 0
}
} else {
this.usersTable.filter = {}
}
this.fetchUsers()
}
},
created() {
this.fetchUsers()
},
mounted() {
this.chart1 = new Chart(this.$refs.chart1.getContext('2d'), {
type: 'bubble',
options: {
layout: {
padding: 10
}
},
data: {
datasets: [
{
label: 'Balance - TX Count in million sats',
backgroundColor: 'rgb(255, 99, 132)',
data: []
}
]
}
})
},
methods: {
formatSat: function (value) {
return LNbits.utils.formatSat(Math.floor(value / 1000))
},
usersTableRowKey: function (row) {
return row.id
},
createUser() {
LNbits.api
.request('POST', '/users/api/v1/user', null, this.createUserDialog.data)
.then(() => {
this.fetchUsers()
this.$q.notify({
type: 'positive',
message: 'Success! User created!',
icon: null
})
})
.catch(function (error) {
LNbits.utils.notifyApiError(error)
})
},
createWallet(user_id) {
LNbits.api
.request(
'POST',
`/users/api/v1/user/${user_id}/wallet`,
null,
this.createWalletDialog.data
)
.then(() => {
this.fetchUsers()
this.$q.notify({
type: 'positive',
message: 'Success! User created!',
icon: null
})
})
.catch(function (error) {
LNbits.utils.notifyApiError(error)
})
},
deleteUser(user_id) {
LNbits.utils
.confirmDialog('Are you sure you want to delete this user?')
.onOk(() => {
LNbits.api
.request('DELETE', `/users/api/v1/user/${user_id}`)
.then(() => {
this.fetchUsers()
this.$q.notify({
type: 'positive',
message: 'Success! User deleted!',
icon: null
})
})
.catch(function (error) {
LNbits.utils.notifyApiError(error)
})
})
},
undeleteUserWallet(user_id, wallet) {
LNbits.api
.request(
'GET',
`/users/api/v1/user/${user_id}/wallet/${wallet}/undelete`
)
.then(() => {
this.fetchWallets(user_id)
this.$q.notify({
type: 'positive',
message: 'Success! Undeleted user wallet!',
icon: null
})
})
.catch(function (error) {
LNbits.utils.notifyApiError(error)
})
},
deleteUserWallet(user_id, wallet, deleted) {
const dialogText = deleted
? 'Wallet is already deleted, are you sure you want to permanently delete this user wallet?'
: 'Are you sure you want to delete this user wallet?'
LNbits.utils.confirmDialog(dialogText).onOk(() => {
LNbits.api
.request('DELETE', `/users/api/v1/user/${user_id}/wallet/${wallet}`)
.then(() => {
this.fetchWallets(user_id)
this.$q.notify({
type: 'positive',
message: 'Success! User wallet deleted!',
icon: null
})
})
.catch(function (error) {
LNbits.utils.notifyApiError(error)
})
})
},
updateChart(users) {
const filtered = users.filter(user => {
if (
user.balance_msat === null ||
user.balance_msat === 0 ||
user.wallet_count === 0
) {
return false
}
return true
})
const data = filtered.map(user => {
return {
x: user.transaction_count,
y: user.balance_msat / 1000000000,
r: 3
}
})
this.chart1.data.datasets[0].data = data
this.chart1.update()
},
fetchUsers(props) {
const params = LNbits.utils.prepareFilterQuery(this.usersTable, props)
LNbits.api
.request('GET', `/users/api/v1/user?${params}`)
.then(res => {
this.usersTable.loading = false
this.usersTable.pagination.rowsNumber = res.data.total
this.users = res.data.data
this.updateChart(this.users)
})
.catch(function (error) {
LNbits.utils.notifyApiError(error)
})
},
fetchWallets(user_id) {
LNbits.api
.request('GET', `/users/api/v1/user/${user_id}/wallet`)
.then(res => {
this.wallets = res.data
this.walletDialog.show = this.wallets.length > 0
if (!this.walletDialog.show) {
this.fetchUsers()
}
})
.catch(function (error) {
LNbits.utils.notifyApiError(error)
})
},
toggleAdmin(user_id) {
LNbits.api
.request('GET', `/users/api/v1/user/${user_id}/admin`)
.then(() => {
this.fetchUsers()
this.$q.notify({
type: 'positive',
message: 'Success! Toggled admin!',
icon: null
})
})
.catch(function (error) {
LNbits.utils.notifyApiError(error)
})
},
exportUsers() {
console.log('export users')
},
topupCallback(res) {
this.wallets.forEach(wallet => {
if (res.wallet_id === wallet.id) {
wallet.balance_msat += res.value * 1000
}
})
this.fetchUsers()
},
topupWallet() {
LNbits.api
.request(
'PUT',
'/users/api/v1/topup',
this.g.user.wallets[0].adminkey,
this.wallet
)
.then(_ => {
this.$q.notify({
type: 'positive',
message: `Success! Added ${this.wallet.amount} to ${this.wallet.id}`,
icon: null
})
this.wallet = {}
})
.catch(function (error) {
LNbits.utils.notifyApiError(error)
})
}
}
})