[feat] user assets (#3504)

This commit is contained in:
Vlad Stan
2025-11-12 14:30:27 +02:00
committed by GitHub
parent c89721223f
commit 39c33699af
28 changed files with 1125 additions and 309 deletions
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+25
View File
@@ -149,6 +149,8 @@ window.localisation.en = {
extensions: 'Extensions',
no_extensions: "You don't have any extensions installed :(",
created: 'Created',
created_at: 'Created At',
updated_at: 'Updated At',
search_extensions: 'Search extensions',
search_wallets: 'Search wallets',
extension_sources: 'Extension Sources',
@@ -159,6 +161,7 @@ window.localisation.en = {
repository: 'Repository',
confirm_continue: 'Are you sure you want to continue?',
manage_extension_details: 'Install/uninstall extension',
upload: 'Upload',
install: 'Install',
uninstall: 'Uninstall',
drop_db: 'Remove Data',
@@ -407,6 +410,8 @@ window.localisation.en = {
first_name: 'First Name',
last_name: 'Last Name',
picture: 'Picture',
user_picture_desc:
'URL to an image to use as profile picture. You can upload it as an asset.',
verify_email: 'Verify email with',
account: 'Account',
update_account: 'Update Account',
@@ -431,6 +436,26 @@ window.localisation.en = {
toggle_gradient: 'Toggle Gradient',
gradient_background: 'Gradient Background',
language: 'Language',
assets: 'Assets',
max_asset_size_mb: 'Max Asset Size (MB)',
max_asset_size_mb_desc:
'The maximum allowed size for asset uploads in megabytes (can use decimal values).',
assets_allowed_mime_types: 'Allowed MIME Types',
assets_allowed_mime_types_desc:
'The MIME types that are allowed for asset uploads. No value means all uploads are allowed.',
thumbnail_width: 'Thumbnail Width',
thumbnail_width_desc: 'Width of the generated thumbnail in pixels.',
thumbnail_height: 'Thumbnail Height',
thumbnail_height_desc: 'Height of the generated thumbnail in pixels.',
thumbnail_format: 'Thumbnail Format',
thumbnail_format_desc:
'Image format of the generated thumbnail (PNG, JPEG, etc.).',
max_assets_per_user: 'Max Assets Per User',
max_assets_per_user_desc:
'The maximum number of assets a user can upload. Zero means upload forbidden.',
assets_no_limit_users: 'Users Without Asset Limits',
assets_no_limit_users_desc:
'These users can upload an unlimited number of assets (user id based).',
color_scheme: 'Color Scheme',
visible_wallet_count: 'Visible Wallet Count',
admin_settings: 'Admin Settings',
@@ -0,0 +1,42 @@
window.app.component('lnbits-admin-assets-config', {
props: ['form-data'],
template: '#lnbits-admin-assets-config',
mixins: [window.windowMixin],
data() {
return {
newAllowedAssetMimeType: '',
newNoLimitUser: ''
}
},
async created() {},
methods: {
addAllowedAssetMimeType() {
if (this.newAllowedAssetMimeType) {
this.removeAllowedAssetMimeType(this.newAllowedAssetMimeType)
this.formData.lnbits_assets_allowed_mime_types.push(
this.newAllowedAssetMimeType
)
this.newAllowedAssetMimeType = ''
}
},
removeAllowedAssetMimeType(type) {
const index = this.formData.lnbits_assets_allowed_mime_types.indexOf(type)
if (index !== -1) {
this.formData.lnbits_assets_allowed_mime_types.splice(index, 1)
}
},
addNewNoLimitUser() {
if (this.newNoLimitUser) {
this.removeNoLimitUser(this.newNoLimitUser)
this.formData.lnbits_assets_no_limit_users.push(this.newNoLimitUser)
this.newNoLimitUser = ''
}
},
removeNoLimitUser(user) {
if (user) {
this.formData.lnbits_assets_no_limit_users =
this.formData.lnbits_assets_no_limit_users.filter(u => u !== user)
}
}
}
})
@@ -1,74 +0,0 @@
window.app.component('lnbits-admin-library', {
props: ['form-data'],
template: '#lnbits-admin-library',
mixins: [window.windowMixin],
data() {
return {
library_images: []
}
},
async created() {
await this.getUploadedImages()
},
methods: {
onImageInput(e) {
const file = e.target.files[0]
if (file) {
this.uploadImage(file)
}
},
uploadImage(file) {
const formData = new FormData()
formData.append('file', file)
LNbits.api
.request(
'POST',
'/admin/api/v1/images',
this.g.user.wallets[0].adminkey,
formData,
{headers: {'Content-Type': 'multipart/form-data'}}
)
.then(() => {
this.$q.notify({
type: 'positive',
message: 'Image uploaded!',
icon: null
})
this.getUploadedImages()
})
.catch(LNbits.utils.notifyApiError)
},
getUploadedImages() {
LNbits.api
.request('GET', '/admin/api/v1/images', this.g.user.wallets[0].inkey)
.then(response => {
this.library_images = response.data.map(image => ({
...image,
url: `${window.origin}/${image.directory}/${image.filename}`
}))
})
.catch(LNbits.utils.notifyApiError)
},
deleteImage(filename) {
LNbits.utils
.confirmDialog('Are you sure you want to delete this image?')
.onOk(() => {
LNbits.api
.request(
'DELETE',
`/admin/api/v1/images/${filename}`,
this.g.user.wallets[0].adminkey
)
.then(() => {
this.$q.notify({
type: 'positive',
message: 'Image deleted!',
icon: null
})
this.getUploadedImages()
})
.catch(LNbits.utils.notifyApiError)
})
}
}
})
+125
View File
@@ -82,6 +82,31 @@ window.PageAccount = {
allRead: false,
allWrite: false
},
assets: [],
assetsTable: {
loading: false,
columns: [
{
name: 'name',
align: 'left',
label: this.$t('Name'),
field: 'name',
sortable: true
},
{
name: 'created_at',
align: 'left',
label: this.$t('created_at'),
field: 'created_at',
sortable: true
}
],
pagination: {
rowsPerPage: 6,
page: 1
}
},
assetsUploadToPublic: false,
notifications: {
nostr: {
identifier: ''
@@ -89,6 +114,17 @@ window.PageAccount = {
}
}
},
watch: {
'assetsTable.search': {
handler() {
const props = {}
if (this.assetsTable.search) {
props['search'] = this.assetsTable.search
}
this.getUserAssets()
}
}
},
methods: {
activeLanguage(lang) {
return window.i18n.global.locale === lang
@@ -400,6 +436,94 @@ window.PageAccount = {
} finally {
this.apiAcl.password = ''
}
},
async getUserAssets(props) {
try {
this.assetsTable.loading = true
const params = LNbits.utils.prepareFilterQuery(this.assetsTable, props)
const {data} = await LNbits.api.request(
'GET',
`/api/v1/assets/paginated?${params}`,
null
)
this.assets = data.data
this.assetsTable.pagination.rowsNumber = data.total
} catch (e) {
LNbits.utils.notifyApiError(e)
} finally {
this.assetsTable.loading = false
}
},
onImageInput(e) {
const file = e.target.files[0]
if (file) {
this.uploadAsset(file)
}
},
async uploadAsset(file) {
const formData = new FormData()
formData.append('file', file)
try {
await LNbits.api.request(
'POST',
`/api/v1/assets?public_asset=${this.assetsUploadToPublic}`,
null,
formData,
{
headers: {'Content-Type': 'multipart/form-data'}
}
)
this.$q.notify({
type: 'positive',
message: 'Upload successful!',
icon: null
})
await this.getUserAssets()
} catch (e) {
console.warn(e)
LNbits.utils.notifyApiError(e)
}
},
async deleteAsset(asset) {
LNbits.utils
.confirmDialog('Are you sure you want to delete this asset?')
.onOk(async () => {
try {
await LNbits.api.request(
'DELETE',
`/api/v1/assets/${asset.id}`,
null
)
this.$q.notify({
type: 'positive',
message: 'Asset deleted.'
})
await this.getUserAssets()
} catch (e) {
console.warn(e)
LNbits.utils.notifyApiError(e)
}
})
},
async toggleAssetPublicAccess(asset) {
try {
await LNbits.api.request('PUT', `/api/v1/assets/${asset.id}`, null, {
is_public: !asset.is_public
})
this.$q.notify({
type: 'positive',
message: 'Update successful!',
icon: null
})
await this.getUserAssets()
} catch (e) {
console.warn(e)
LNbits.utils.notifyApiError(e)
}
},
copyAssetLinkToClipboard(asset) {
const assetUrl = `${window.location.origin}/api/v1/assets/${asset.id}/binary`
this.copyText(assetUrl)
}
},
@@ -417,5 +541,6 @@ window.PageAccount = {
this.tab = hash
}
await this.getApiACLs()
await this.getUserAssets()
}
}
+1 -1
View File
@@ -65,7 +65,7 @@
"js/components/admin/lnbits-admin-extensions.js",
"js/components/admin/lnbits-admin-notifications.js",
"js/components/admin/lnbits-admin-site-customisation.js",
"js/components/admin/lnbits-admin-library.js",
"js/components/admin/lnbits-admin-assets-config.js",
"js/components/admin/lnbits-admin-audit.js",
"js/components/lnbits-home-logos.js",
"js/components/lnbits-new-user-wallet.js",