refactor: g.settings with a PublicSettings class for mapping, for reactive WINDOW_SETTINGS (#3644)
This commit is contained in:
+8
-54
@@ -70,60 +70,11 @@ def template_renderer(additional_folders: list | None = None) -> Jinja2Templates
|
||||
t.env.globals["static_url_for"] = static_url_for
|
||||
t.env.globals["normalize_path"] = normalize_path
|
||||
|
||||
window_settings = {
|
||||
"AD_SPACE": settings.lnbits_ad_space,
|
||||
"AD_SPACE_ENABLED": settings.lnbits_ad_space_enabled,
|
||||
"AD_SPACE_TITLE": settings.lnbits_ad_space_title,
|
||||
"EXTENSIONS": list(settings.lnbits_installed_extensions_ids),
|
||||
"HIDE_API": settings.lnbits_hide_api,
|
||||
"SITE_TITLE": settings.lnbits_site_title,
|
||||
"SITE_TAGLINE": settings.lnbits_site_tagline,
|
||||
"SITE_DESCRIPTION": settings.lnbits_site_description,
|
||||
"LNBITS_ADMIN_UI": settings.lnbits_admin_ui,
|
||||
"LNBITS_AUDIT_ENABLED": settings.lnbits_audit_enabled,
|
||||
"LNBITS_AUTH_METHODS": settings.auth_allowed_methods,
|
||||
"LNBITS_AUTH_KEYCLOAK_ORG": settings.keycloak_client_custom_org,
|
||||
"LNBITS_AUTH_KEYCLOAK_ICON": settings.keycloak_client_custom_icon,
|
||||
"LNBITS_CUSTOM_IMAGE": settings.lnbits_custom_image,
|
||||
"LNBITS_CUSTOM_BADGE": settings.lnbits_custom_badge,
|
||||
"LNBITS_CUSTOM_BADGE_COLOR": settings.lnbits_custom_badge_color,
|
||||
"LNBITS_EXTENSIONS_DEACTIVATE_ALL": settings.lnbits_extensions_deactivate_all,
|
||||
"LNBITS_NEW_ACCOUNTS_ALLOWED": settings.new_accounts_allowed,
|
||||
"LNBITS_NODE_UI": settings.lnbits_node_ui and settings.has_nodemanager,
|
||||
"LNBITS_NODE_UI_AVAILABLE": settings.has_nodemanager,
|
||||
"LNBITS_QR_LOGO": settings.lnbits_qr_logo,
|
||||
"LNBITS_APPLE_TOUCH_ICON": settings.lnbits_apple_touch_icon,
|
||||
"LNBITS_SERVICE_FEE": settings.lnbits_service_fee,
|
||||
"LNBITS_SERVICE_FEE_MAX": settings.lnbits_service_fee_max,
|
||||
"LNBITS_SERVICE_FEE_WALLET": settings.lnbits_service_fee_wallet,
|
||||
"LNBITS_SHOW_HOME_PAGE_ELEMENTS": settings.lnbits_show_home_page_elements,
|
||||
"LNBITS_THEME_OPTIONS": settings.lnbits_theme_options,
|
||||
"WALLET_FEATURED_BUTTON_LABEL": settings.lnbits_wallet_featured_button_label,
|
||||
"WALLET_FEATURED_BUTTON_URL": settings.lnbits_wallet_featured_button_url,
|
||||
"WALLET_FEATURED_BUTTON_ICON": settings.lnbits_wallet_featured_button_icon,
|
||||
"LNBITS_VERSION": settings.version,
|
||||
"USE_CUSTOM_LOGO": settings.lnbits_custom_logo,
|
||||
"LNBITS_DEFAULT_REACTION": settings.lnbits_default_reaction,
|
||||
"LNBITS_DEFAULT_THEME": settings.lnbits_default_theme,
|
||||
"LNBITS_DEFAULT_BORDER": settings.lnbits_default_border,
|
||||
"LNBITS_DEFAULT_GRADIENT": settings.lnbits_default_gradient,
|
||||
"LNBITS_DEFAULT_BGIMAGE": settings.lnbits_default_bgimage,
|
||||
"VOIDWALLET": settings.lnbits_backend_wallet_class == "VoidWallet",
|
||||
"WEBPUSH_PUBKEY": settings.lnbits_webpush_pubkey,
|
||||
"LNBITS_EXTENSIONS_REVIEWS_URL": settings.lnbits_extensions_reviews_url,
|
||||
"LNBITS_DENOMINATION": settings.lnbits_denomination,
|
||||
"has_holdinvoice": settings.has_holdinvoice,
|
||||
"LNBITS_NOSTR_CONFIGURED": settings.is_nostr_notifications_configured(),
|
||||
"LNBITS_TELEGRAM_CONFIGURED": settings.is_telegram_notifications_configured(),
|
||||
"LNBITS_EXT_BUILDER": settings.lnbits_extensions_builder_activate_non_admins,
|
||||
"LNBITS_CURRENCIES": list(currencies.keys()),
|
||||
"LNBITS_ALLOWED_CURRENCIES": settings.lnbits_allowed_currencies,
|
||||
"CACHE_KEY": settings.server_startup_time,
|
||||
}
|
||||
|
||||
t.env.globals["WINDOW_SETTINGS"] = window_settings
|
||||
for key, value in window_settings.items():
|
||||
t.env.globals[key] = value
|
||||
# used in base.html
|
||||
t.env.globals["SITE_TITLE"] = settings.lnbits_site_title
|
||||
t.env.globals["LNBITS_APPLE_TOUCH_ICON"] = settings.lnbits_apple_touch_icon
|
||||
t.env.globals["SETTINGS"] = settings.to_public().dict(by_alias=True)
|
||||
t.env.globals["CURRENCIES"] = list(currencies.keys())
|
||||
|
||||
if settings.bundle_assets:
|
||||
t.env.globals["INCLUDED_JS"] = ["bundle.min.js"]
|
||||
@@ -137,6 +88,9 @@ def template_renderer(additional_folders: list | None = None) -> Jinja2Templates
|
||||
t.env.globals["INCLUDED_CSS"] = vendor_files["css"]
|
||||
t.env.globals["INCLUDED_COMPONENTS"] = vendor_files["components"]
|
||||
|
||||
# backwards compatibility for extensions (tpos)
|
||||
t.env.globals["LNBITS_DENOMINATION"] = settings.lnbits_denomination
|
||||
|
||||
return t
|
||||
|
||||
|
||||
|
||||
@@ -1131,6 +1131,115 @@ class Settings(EditableSettings, ReadOnlySettings, TransientSettings, BaseSettin
|
||||
and ext_id in self.lnbits_all_extensions_ids
|
||||
)
|
||||
|
||||
def to_public(self) -> PublicSettings:
|
||||
return PublicSettings.from_settings(self)
|
||||
|
||||
|
||||
class PublicSettings(BaseModel):
|
||||
"""
|
||||
PublicSettings is used for templating and public information.
|
||||
WARNING: do not expose private information, PublicSettings is exposed publicly.
|
||||
"""
|
||||
|
||||
allow_register: bool = Field(alias="allowRegister")
|
||||
qr_logo: str = Field(alias="qrLogo")
|
||||
site_title: str = Field(alias="siteTitle")
|
||||
site_tagline: str = Field(alias="siteTagline")
|
||||
site_description: str | None = Field(alias="siteDescription")
|
||||
auth_methods: list[str] = Field(alias="authMethods")
|
||||
keycloak_org: str | None = Field(alias="keycloakOrg")
|
||||
keycloak_icon: str | None = Field(alias="keycloakIcon")
|
||||
has_holdinvoice: bool = Field(alias="hasHoldinvoice")
|
||||
has_nodemanager: bool = Field(alias="hasNodemanager")
|
||||
show_nodemanager: bool = Field(alias="showNodemanager")
|
||||
custom_logo: str | None = Field(alias="customLogo")
|
||||
show_voidwallet: bool = Field(alias="showVoidwallet")
|
||||
version: str = Field(alias="version")
|
||||
show_home_page_elements: bool = Field(alias="showHomePageElements")
|
||||
hide_api: bool = Field(alias="hideApi")
|
||||
cache_key: str = Field(alias="cacheKey")
|
||||
webpush_pubkey: str | None = Field(alias="webpushPubkey")
|
||||
show_extensions: bool = Field(alias="showExtensions")
|
||||
show_audit: bool = Field(alias="showAudit")
|
||||
show_admin: bool = Field(alias="showAdmin")
|
||||
ad_space: list[list[str]] = Field(alias="adSpace")
|
||||
ad_space_title: str = Field(alias="adSpaceTitle")
|
||||
show_ad_space: bool = Field(alias="showAdSpace")
|
||||
custom_image: str | None = Field(alias="customImage")
|
||||
custom_badge: str | None = Field(alias="customBadge")
|
||||
custom_badge_color: str | None = Field(alias="customBadgeColor")
|
||||
service_fee: float = Field(alias="serviceFee")
|
||||
service_fee_max: int = Field(alias="serviceFeeMax")
|
||||
service_fee_wallet: str | None = Field(alias="serviceFeeWallet")
|
||||
theme_options: list[str] = Field(alias="themeOptions")
|
||||
default_reaction: str = Field(alias="defaultReaction")
|
||||
default_theme: str = Field(alias="defaultTheme")
|
||||
default_border: str = Field(alias="defaultBorder")
|
||||
default_bgimage: str | None = Field(alias="defaultBgimage")
|
||||
default_gradient: bool = Field(alias="defaultGradient")
|
||||
denomination: str | None = Field()
|
||||
extensions: list[str] = Field()
|
||||
allowed_currencies: list[str] = Field(alias="allowedCurrencies")
|
||||
extensions_reviews_url: str = Field(alias="extensionsReviewsUrl")
|
||||
ext_builder: bool = Field(alias="extBuilder")
|
||||
nostr_configured: bool = Field(alias="nostrConfigured")
|
||||
telegram_configured: bool = Field(alias="telegramConfigured")
|
||||
wallet_featured_button_label: str | None = Field(alias="walletFeaturedButtonLabel")
|
||||
wallet_featured_button_url: str | None = Field(alias="walletFeaturedButtonUrl")
|
||||
wallet_featured_button_icon: str | None = Field(alias="walletFeaturedButtonIcon")
|
||||
|
||||
@classmethod
|
||||
def from_settings(cls, settings: Settings):
|
||||
ads = [x.split(";") for x in settings.lnbits_ad_space.split(",")]
|
||||
return cls(
|
||||
adSpace=ads,
|
||||
adSpaceTitle=settings.lnbits_ad_space_title,
|
||||
showAdSpace=settings.lnbits_ad_space_enabled and len(ads) > 0,
|
||||
allowRegister=settings.new_accounts_allowed,
|
||||
qrLogo=settings.lnbits_qr_logo,
|
||||
siteDescription=settings.lnbits_site_description,
|
||||
siteTitle=settings.lnbits_site_title,
|
||||
siteTagline=settings.lnbits_site_tagline,
|
||||
authMethods=settings.auth_allowed_methods,
|
||||
keycloakOrg=settings.keycloak_client_custom_org,
|
||||
keycloakIcon=settings.keycloak_client_custom_icon,
|
||||
hasHoldinvoice=settings.has_holdinvoice,
|
||||
hasNodemanager=settings.has_nodemanager,
|
||||
showNodemanager=settings.lnbits_node_ui and settings.has_nodemanager,
|
||||
customLogo=settings.lnbits_custom_logo,
|
||||
showVoidwallet=settings.lnbits_backend_wallet_class == "VoidWallet",
|
||||
version=settings.version,
|
||||
showHomePageElements=settings.lnbits_show_home_page_elements,
|
||||
hideApi=settings.lnbits_hide_api,
|
||||
cacheKey=str(settings.server_startup_time),
|
||||
webpushPubkey=settings.lnbits_webpush_pubkey,
|
||||
showExtensions=not settings.lnbits_extensions_deactivate_all,
|
||||
showAudit=settings.lnbits_audit_enabled,
|
||||
showAdmin=settings.lnbits_admin_ui,
|
||||
customImage=settings.lnbits_custom_image,
|
||||
customBadge=settings.lnbits_custom_badge,
|
||||
customBadgeColor=settings.lnbits_custom_badge_color,
|
||||
serviceFee=settings.lnbits_service_fee,
|
||||
serviceFeeMax=settings.lnbits_service_fee_max,
|
||||
serviceFeeWallet=settings.lnbits_service_fee_wallet,
|
||||
themeOptions=settings.lnbits_theme_options,
|
||||
defaultReaction=settings.lnbits_default_reaction,
|
||||
defaultTheme=settings.lnbits_default_theme,
|
||||
defaultBorder=settings.lnbits_default_border,
|
||||
defaultGradient=settings.lnbits_default_gradient,
|
||||
defaultBgimage=settings.lnbits_default_bgimage,
|
||||
denomination=settings.lnbits_denomination,
|
||||
extensions=list(settings.lnbits_installed_extensions_ids),
|
||||
allowedCurrencies=settings.lnbits_allowed_currencies,
|
||||
extensionsReviewsUrl=settings.lnbits_extensions_reviews_url,
|
||||
extBuilder=settings.lnbits_extensions_builder_activate_non_admins,
|
||||
nostrConfigured=settings.is_nostr_notifications_configured(),
|
||||
telegramConfigured=settings.is_telegram_notifications_configured(),
|
||||
walletFeaturedButtonLabel=settings.lnbits_wallet_featured_button_label,
|
||||
walletFeaturedButtonUrl=settings.lnbits_wallet_featured_button_url,
|
||||
walletFeaturedButtonIcon=settings.lnbits_wallet_featured_button_icon,
|
||||
)
|
||||
|
||||
|
||||
class SuperSettings(EditableSettings):
|
||||
super_user: str
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+10
-10
File diff suppressed because one or more lines are too long
@@ -35,23 +35,6 @@ window.app.component('lnbits-extension-rating', {
|
||||
|
||||
window.app.component('lnbits-manage', {
|
||||
template: '#lnbits-manage',
|
||||
computed: {
|
||||
showAdmin() {
|
||||
return this.LNBITS_ADMIN_UI
|
||||
},
|
||||
showUsers() {
|
||||
return this.LNBITS_ADMIN_UI
|
||||
},
|
||||
showNode() {
|
||||
return this.LNBITS_NODE_UI
|
||||
},
|
||||
showAudit() {
|
||||
return this.LNBITS_AUDIT_ENABLED
|
||||
},
|
||||
showExtensions() {
|
||||
return this.LNBITS_EXTENSIONS_DEACTIVATE_ALL === false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
isActive(path) {
|
||||
return window.location.pathname === path
|
||||
@@ -462,9 +445,7 @@ window.app.component('username-password', {
|
||||
username: this.userName,
|
||||
password: this.password_1,
|
||||
passwordRepeat: this.password_2,
|
||||
reset_key: this.resetKey,
|
||||
keycloakOrg: LNBITS_AUTH_KEYCLOAK_ORG || 'Keycloak',
|
||||
keycloakIcon: LNBITS_AUTH_KEYCLOAK_ICON
|
||||
reset_key: this.resetKey
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
window.app.component('lnbits-footer', {
|
||||
template: '#lnbits-footer',
|
||||
computed: {
|
||||
version() {
|
||||
return this.LNBITS_VERSION || 'unknown version'
|
||||
},
|
||||
title() {
|
||||
return `${this.SITE_TITLE}, ${this.SITE_TAGLINE}`
|
||||
return `${this.g.settings.siteTitle}, ${this.g.settings.siteTagline}`
|
||||
},
|
||||
showFooter() {
|
||||
return this.LNBITS_SHOW_HOME_PAGE_ELEMENTS == true
|
||||
version() {
|
||||
return this.$t('lnbits_version') + ': ' + this.g.settings.version
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,48 +1,9 @@
|
||||
window.app.component('lnbits-header', {
|
||||
template: '#lnbits-header',
|
||||
computed: {
|
||||
hasServiceFeeMax() {
|
||||
return (
|
||||
this.g.user &&
|
||||
this.LNBITS_SERVICE_FEE_MAX &&
|
||||
this.LNBITS_SERVICE_FEE_MAX > 0
|
||||
)
|
||||
},
|
||||
serviceFeeMax() {
|
||||
return this.LNBITS_SERVICE_FEE_MAX || 0
|
||||
},
|
||||
hasServiceFee() {
|
||||
return (
|
||||
this.g.user && this.LNBITS_SERVICE_FEE && this.LNBITS_SERVICE_FEE > 0
|
||||
)
|
||||
},
|
||||
serviceFee() {
|
||||
return this.LNBITS_SERVICE_FEE || 0
|
||||
},
|
||||
hasCustomBadge() {
|
||||
return this.LNBITS_CUSTOM_BADGE && this.LNBITS_CUSTOM_BADGE != ''
|
||||
},
|
||||
customBadge() {
|
||||
return this.LNBITS_CUSTOM_BADGE || ''
|
||||
},
|
||||
customBadgeColor() {
|
||||
return this.LNBITS_CUSTOM_BADGE_COLOR || ''
|
||||
},
|
||||
title() {
|
||||
return this.SITE_TITLE
|
||||
},
|
||||
titleIsLnbits() {
|
||||
return this.SITE_TITLE == 'LNbits'
|
||||
},
|
||||
customLogoUrl() {
|
||||
return this.USE_CUSTOM_LOGO || null
|
||||
},
|
||||
showAdmin() {
|
||||
return this.g.user && (this.g.user.super_user || this.g.user.admin)
|
||||
},
|
||||
showVoidwallet() {
|
||||
return this.g.user && this.VOIDWALLET == true
|
||||
},
|
||||
displayName() {
|
||||
return (
|
||||
this.g.user?.extra?.display_name ||
|
||||
|
||||
@@ -95,8 +95,8 @@ window.app.component('lnbits-home-logos', {
|
||||
showLogos() {
|
||||
return (
|
||||
this.g.isSatsDenomination &&
|
||||
this.SITE_TITLE == 'LNbits' &&
|
||||
this.LNBITS_SHOW_HOME_PAGE_ELEMENTS == true
|
||||
this.g.settings.siteTitle == 'LNbits' &&
|
||||
this.g.settings.showHomePageElements == true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ window.app.component('lnbits-qrcode', {
|
||||
},
|
||||
logo: {
|
||||
type: String,
|
||||
default: LNBITS_QR_LOGO
|
||||
default: window.g.settings.qrLogo || null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
|
||||
+35
-40
@@ -9,26 +9,22 @@ const localStore = (key, defaultValue) => {
|
||||
}
|
||||
|
||||
window.g = Vue.reactive({
|
||||
errorCode: null,
|
||||
errorMessage: null,
|
||||
user: null,
|
||||
wallet: null,
|
||||
isPublicPage: true,
|
||||
isUserAuthorized: !!Quasar.Cookies.get('is_lnbits_user_authorized'),
|
||||
isUserImpersonated: !!Quasar.Cookies.get('is_lnbits_user_impersonated'),
|
||||
offline: !navigator.onLine,
|
||||
hasCamera: false,
|
||||
visibleDrawer: false,
|
||||
extensions: WINDOW_SETTINGS.EXTENSIONS,
|
||||
fiatBalance: 0,
|
||||
exchangeRate: 0,
|
||||
fiatTracking: false,
|
||||
payments: [],
|
||||
walletEventListeners: [],
|
||||
updatePayments: false, // used for updating the lnbits-payment-list
|
||||
updatePaymentsHash: false, // used for closing the receive dialog
|
||||
currencies: WINDOW_SETTINGS.LNBITS_CURRENCIES ?? [],
|
||||
allowedCurrencies: WINDOW_SETTINGS.LNBITS_ALLOWED_CURRENCIES ?? [],
|
||||
// vars from server
|
||||
settings: SETTINGS,
|
||||
currencies: CURRENCIES,
|
||||
extensions: SETTINGS.extensions,
|
||||
allowedCurrencies: SETTINGS.allowedCurrencies,
|
||||
denomination: SETTINGS.denomination,
|
||||
isSatsDenomination: SETTINGS.denomination == 'sats',
|
||||
// local storage vars
|
||||
themeChoice: localStore('lnbits.theme', SETTINGS.defaultTheme),
|
||||
borderChoice: localStore('lnbits.border', SETTINGS.defaultBorder),
|
||||
gradientChoice: localStore('lnbits.gradientBg', SETTINGS.defaultGradient),
|
||||
reactionChoice: localStore('lnbits.reactions', SETTINGS.defaultReaction),
|
||||
bgimageChoice: localStore(
|
||||
'lnbits.backgroundImage',
|
||||
SETTINGS.defaultBgimage || ''
|
||||
),
|
||||
locale: localStore('lnbits.lang', navigator.languages[1] ?? 'en'),
|
||||
disclaimerShown: localStore('lnbits.disclaimerShown', false),
|
||||
isFiatPriority: localStore('lnbits.isFiatPriority', false),
|
||||
@@ -36,26 +32,25 @@ window.g = Vue.reactive({
|
||||
walletFlip: localStore('lnbits.walletFlip', false),
|
||||
lastActiveWallet: localStore('lnbits.lastActiveWallet', null),
|
||||
darkChoice: localStore('lnbits.darkMode', true),
|
||||
themeChoice: localStore('lnbits.theme', WINDOW_SETTINGS.LNBITS_DEFAULT_THEME),
|
||||
borderChoice: localStore(
|
||||
'lnbits.border',
|
||||
WINDOW_SETTINGS.LNBITS_DEFAULT_BORDER || 'hard-border'
|
||||
),
|
||||
gradientChoice: localStore(
|
||||
'lnbits.gradientBg',
|
||||
WINDOW_SETTINGS.LNBITS_DEFAULT_GRADIENT || false
|
||||
),
|
||||
reactionChoice: localStore(
|
||||
'lnbits.reactions',
|
||||
WINDOW_SETTINGS.LNBITS_DEFAULT_REACTION || 'confettiBothSides'
|
||||
),
|
||||
bgimageChoice: localStore(
|
||||
'lnbits.backgroundImage',
|
||||
WINDOW_SETTINGS.LNBITS_DEFAULT_BGIMAGE || ''
|
||||
),
|
||||
ads: WINDOW_SETTINGS.AD_SPACE.split(',').map(ad => ad.split(';')),
|
||||
denomination: WINDOW_SETTINGS.LNBITS_DENOMINATION,
|
||||
isSatsDenomination: WINDOW_SETTINGS.LNBITS_DENOMINATION == 'sats',
|
||||
// cookie vars
|
||||
isUserAuthorized: !!Quasar.Cookies.get('is_lnbits_user_authorized'),
|
||||
isUserImpersonated: !!Quasar.Cookies.get('is_lnbits_user_impersonated'),
|
||||
// frontend vars
|
||||
errorCode: null,
|
||||
errorMessage: null,
|
||||
user: null,
|
||||
wallet: null,
|
||||
isPublicPage: true,
|
||||
offline: !navigator.onLine,
|
||||
hasCamera: false,
|
||||
visibleDrawer: false,
|
||||
fiatBalance: 0,
|
||||
exchangeRate: 0,
|
||||
fiatTracking: false,
|
||||
payments: [],
|
||||
walletEventListeners: [],
|
||||
updatePayments: false, // used for updating the lnbits-payment-list
|
||||
updatePaymentsHash: false, // used for closing the receive dialog
|
||||
scanner: null,
|
||||
newWalletType: null
|
||||
})
|
||||
|
||||
@@ -157,8 +157,7 @@ window.app.mixin({
|
||||
return {
|
||||
api: window._lnbitsApi,
|
||||
utils: window._lnbitsUtils,
|
||||
g: window.g,
|
||||
...WINDOW_SETTINGS
|
||||
g: window.g
|
||||
}
|
||||
},
|
||||
// backwards compatibility for extensions, should not be used in the future
|
||||
|
||||
@@ -711,7 +711,7 @@ window.PageAccount = {
|
||||
await this.getUserAssets()
|
||||
// filter out themes that are not allowed
|
||||
this.themeOptions = this.themeOptions.filter(theme =>
|
||||
this.LNBITS_THEME_OPTIONS.includes(theme.name)
|
||||
this.g.settings.themeOptions.includes(theme.name)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -842,8 +842,8 @@ window.PageExtensions = {
|
||||
},
|
||||
async created() {
|
||||
this.extensions = await this.fetchAllExtensions()
|
||||
this.extbuilderEnabled = this.g.user.admin || this.LNBITS_EXT_BUILDER
|
||||
this.reviewsUrl = this.LNBITS_EXTENSIONS_REVIEWS_URL
|
||||
this.extbuilderEnabled = this.g.user.admin || this.g.settings.extBuilder
|
||||
this.reviewsUrl = this.g.settings.extensionsReviewsUrl
|
||||
|
||||
if (this.g.user.extensions.length === 0) {
|
||||
this.tab = 'all'
|
||||
|
||||
@@ -19,40 +19,15 @@ window.PageHome = {
|
||||
showClaimLnurl() {
|
||||
return (
|
||||
this.lnurl !== '' &&
|
||||
this.allowRegister &&
|
||||
'user-id-only' in this.LNBITS_AUTH_METHODS
|
||||
this.g.settings.allowRegister &&
|
||||
'user-id-only' in this.g.settings.authMethods
|
||||
)
|
||||
},
|
||||
formatDescription() {
|
||||
return LNbits.utils.convertMarkdown(this.SITE_DESCRIPTION)
|
||||
return LNbits.utils.convertMarkdown(this.g.settings.siteDescription)
|
||||
},
|
||||
isAccessTokenExpired() {
|
||||
return this.$q.cookies.get('is_access_token_expired')
|
||||
},
|
||||
allowRegister() {
|
||||
return this.LNBITS_NEW_ACCOUNTS_ALLOWED
|
||||
},
|
||||
// TODO: this makes no sense
|
||||
hasCustomImage() {
|
||||
return this.LNBITS_CUSTOM_IMAGE
|
||||
},
|
||||
showHomepageElements() {
|
||||
return this.LNBITS_SHOW_HOME_PAGE_ELEMENTS === true
|
||||
},
|
||||
siteTitle() {
|
||||
return this.SITE_TITLE || ''
|
||||
},
|
||||
siteTagline() {
|
||||
return this.SITE_TAGLINE || ''
|
||||
},
|
||||
adsEnabled() {
|
||||
return this.AD_SPACE_ENABLED && this.AD_SPACE && this.AD_SPACE.length > 0
|
||||
},
|
||||
adsTitle() {
|
||||
return this.AD_SPACE_TITLE || ''
|
||||
},
|
||||
ads() {
|
||||
return this.AD_SPACE.map(ad => ad.split(';'))
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
window._lnbitsUtils = {
|
||||
url_for(url) {
|
||||
const _url = new URL(url, window.location.origin)
|
||||
_url.searchParams.set('v', WINDOW_SETTINGS.CACHE_KEY)
|
||||
_url.searchParams.set('v', window.g.settings.cacheKey)
|
||||
return _url.toString()
|
||||
},
|
||||
loadScript(src) {
|
||||
|
||||
@@ -70,10 +70,10 @@
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
const RENDERED_ROUTE = '{{ normalize_path(request.path) }}'
|
||||
const WINDOW_SETTINGS = {{ WINDOW_SETTINGS | tojson }}
|
||||
Object.keys(WINDOW_SETTINGS).forEach(key => {
|
||||
window[key] = WINDOW_SETTINGS[key]
|
||||
})
|
||||
const SETTINGS = {{ SETTINGS | tojson | safe }}
|
||||
const CURRENCIES = {{ CURRENCIES | tojson | safe }}
|
||||
// backwards compatibility for extensions (tpos)
|
||||
const LNBITS_DENOMINATION = {{ LNBITS_DENOMINATION | tojson | safe }}
|
||||
</script>
|
||||
<!-- vue templates -->
|
||||
{% include('components.vue') %} {% include('pages.vue') %}
|
||||
|
||||
@@ -36,7 +36,7 @@ include('components/lnbits-error.vue') %}
|
||||
<q-list v-if="g.user" dense class="lnbits-drawer__q-list">
|
||||
<q-item-label header v-text="$t('manage')"></q-item-label>
|
||||
<div v-if="g.user.admin">
|
||||
<q-item v-if="showAdmin" to="/admin">
|
||||
<q-item v-if="g.settings.showAdmin" to="/admin">
|
||||
<q-item-section side>
|
||||
<q-icon
|
||||
name="settings"
|
||||
@@ -51,7 +51,7 @@ include('components/lnbits-error.vue') %}
|
||||
<q-icon name="chevron_right" color="grey-5" size="md"></q-icon>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item v-if="showNode" to="/node">
|
||||
<q-item v-if="g.settings.showNodemanager" to="/node">
|
||||
<q-item-section side>
|
||||
<q-icon
|
||||
name="developer_board"
|
||||
@@ -66,7 +66,7 @@ include('components/lnbits-error.vue') %}
|
||||
<q-icon name="chevron_right" color="grey-5" size="md"></q-icon>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item v-if="showUsers" to="/users">
|
||||
<q-item v-if="g.settings.showAdmin" to="/users">
|
||||
<q-item-section side>
|
||||
<q-icon
|
||||
name="groups"
|
||||
@@ -81,7 +81,7 @@ include('components/lnbits-error.vue') %}
|
||||
<q-icon name="chevron_right" color="grey-5" size="md"></q-icon>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item v-if="showAudit" to="/audit">
|
||||
<q-item v-if="g.settings.showAudit" to="/audit">
|
||||
<q-item-section side>
|
||||
<q-icon
|
||||
name="playlist_add_check_circle"
|
||||
@@ -112,7 +112,7 @@ include('components/lnbits-error.vue') %}
|
||||
<q-icon name="chevron_right" color="grey-5" size="md"></q-icon>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item v-if="showExtensions" to="/extensions">
|
||||
<q-item v-if="g.settings.showExtensions" to="/extensions">
|
||||
<q-item-section side>
|
||||
<q-icon
|
||||
name="extension"
|
||||
@@ -686,7 +686,7 @@ include('components/lnbits-error.vue') %}
|
||||
dense
|
||||
filled
|
||||
v-model="walletName"
|
||||
:label="$t('name_your_wallet', {name: SITE_TITLE + ' *'})"
|
||||
:label="$t('name_your_wallet', {name: g.settings.siteTitle + ' *'})"
|
||||
></q-input>
|
||||
<q-card-actions vertical align="center" class="q-pa-none">
|
||||
<q-btn
|
||||
@@ -960,9 +960,9 @@ include('components/lnbits-error.vue') %}
|
||||
<q-avatar size="32px" class="q-mr-md">
|
||||
<q-img
|
||||
:src="
|
||||
keycloakIcon
|
||||
? keycloakIcon
|
||||
: 'lnbits/static/images/keycloak-logo.png'
|
||||
g.settings.keycloakIcon
|
||||
? g.settings.keycloakIcon
|
||||
: utils.url_for('lnbits/static/images/keycloak-logo.png')
|
||||
"
|
||||
></q-img>
|
||||
</q-avatar>
|
||||
@@ -970,7 +970,7 @@ include('components/lnbits-error.vue') %}
|
||||
<span
|
||||
v-text="
|
||||
$t('signin_with_custom_org', {
|
||||
custom_org: keycloakOrg
|
||||
custom_org: g.settings.keycloakOrg || 'Keycloak'
|
||||
})
|
||||
"
|
||||
></span>
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
<br />
|
||||
</div>
|
||||
<div class="col">
|
||||
<div v-if="LNBITS_NODE_UI">
|
||||
<div v-if="g.settings.hasNodemanager">
|
||||
<p>
|
||||
<span v-text="$t('node_management')"></span>
|
||||
</p>
|
||||
@@ -70,7 +70,7 @@
|
||||
v-model="formData.lnbits_node_ui_transactions"
|
||||
></q-toggle>
|
||||
</div>
|
||||
<p v-if="!LNBITS_NODE_UI">
|
||||
<p v-if="!g.settings.hasNodemanager">
|
||||
<span v-text="$t('node_management_not_supported')"></span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template id="lnbits-footer">
|
||||
<q-footer
|
||||
v-if="showFooter"
|
||||
v-if="g.settings.showHomePageElements"
|
||||
class="bg-transparent q-px-lg q-py-md"
|
||||
:class="{'text-dark': !$q.dark.isActive}"
|
||||
>
|
||||
@@ -9,7 +9,7 @@
|
||||
<q-toolbar-title class="text-caption">
|
||||
<span v-text="title"></span>
|
||||
<br />
|
||||
<small v-text="$t('lnbits_version') + ': ' + version"></small>
|
||||
<small v-text="version"></small>
|
||||
</q-toolbar-title>
|
||||
<q-space></q-space>
|
||||
<q-btn
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
<template id="lnbits-header">
|
||||
<q-header bordered class="bg-marginal-bg">
|
||||
<q-banner v-if="showVoidwallet" class="bg-warning text-white" dense>
|
||||
<q-banner
|
||||
v-if="g.settings.showVoidwallet"
|
||||
class="bg-warning text-white"
|
||||
dense
|
||||
>
|
||||
<template v-slot:avatar>
|
||||
<q-icon name="warning" color="white" />
|
||||
</template>
|
||||
@@ -24,13 +28,15 @@
|
||||
<q-toolbar-title>
|
||||
<q-btn flat no-caps dense class="q-mr-sm" size="lg" type="a" href="/">
|
||||
<q-img
|
||||
v-if="customLogoUrl"
|
||||
v-if="customLogo"
|
||||
height="30px"
|
||||
alt="Logo"
|
||||
:src="customLogoUrl"
|
||||
:src="customLogo"
|
||||
></q-img>
|
||||
<span v-else-if="!titleIsLnbits"><strong>LN</strong>bits</span>
|
||||
<span v-else v-text="title"></span>
|
||||
<span v-else-if="g.settings.siteTitle == 'LNbits'"
|
||||
><strong>LN</strong>bits</span
|
||||
>
|
||||
<span v-else v-text="g.settings.siteTitle"></span>
|
||||
</q-btn>
|
||||
<q-badge v-if="g.user && g.user.super_user">Super User</q-badge>
|
||||
<q-badge v-else-if="g.user && g.user.admin">Admin User</q-badge>
|
||||
@@ -38,31 +44,31 @@
|
||||
<q-badge
|
||||
class="q-mr-md"
|
||||
v-show="$q.screen.gt.sm"
|
||||
v-if="hasCustomBadge"
|
||||
:label="customBadge"
|
||||
:color="customBadgeColor"
|
||||
v-if="g.settings.customBadge"
|
||||
:label="g.settings.customBadge"
|
||||
:color="g.settings.customBadgeColor"
|
||||
>
|
||||
</q-badge>
|
||||
|
||||
<q-badge
|
||||
v-show="$q.screen.gt.sm"
|
||||
v-if="hasServiceFee"
|
||||
v-if="g.user && g.settings.serviceFee > 0"
|
||||
color="green"
|
||||
class="q-mr-md"
|
||||
>
|
||||
<span
|
||||
v-if="hasServiceFeeMax"
|
||||
v-if="g.user && g.settings.serviceFeeMax > 0"
|
||||
v-text="
|
||||
$t('service_fee_max_badge', {
|
||||
amount: serviceFee,
|
||||
max: serviceFeeMax,
|
||||
amount: g.settings.serviceFee,
|
||||
max: g.settings.serviceFeeMax,
|
||||
denom: g.denomination
|
||||
})
|
||||
"
|
||||
></span>
|
||||
<span
|
||||
v-else
|
||||
v-text="$t('service_fee_badge', {amount: serviceFee})"
|
||||
v-text="$t('service_fee_badge', {amount: g.settings.serviceFee})"
|
||||
></span>
|
||||
<q-tooltip><span v-text="$t('service_fee_tooltip')"></span></q-tooltip>
|
||||
</q-badge>
|
||||
|
||||
@@ -118,7 +118,7 @@
|
||||
</q-list>
|
||||
</q-card-section>
|
||||
<q-expansion-item
|
||||
v-if="!HIDE_API"
|
||||
v-if="!g.settings.hideApi"
|
||||
group="api"
|
||||
dense
|
||||
expand-separator
|
||||
@@ -152,7 +152,7 @@
|
||||
</q-expansion-item>
|
||||
|
||||
<q-expansion-item
|
||||
v-if="!HIDE_API"
|
||||
v-if="!g.settings.hideApi"
|
||||
group="api"
|
||||
dense
|
||||
expand-separator
|
||||
@@ -197,7 +197,7 @@
|
||||
</q-card>
|
||||
</q-expansion-item>
|
||||
<q-expansion-item
|
||||
v-if="!HIDE_API"
|
||||
v-if="!g.settings.hideApi"
|
||||
group="api"
|
||||
dense
|
||||
expand-separator
|
||||
@@ -244,7 +244,7 @@
|
||||
</q-expansion-item>
|
||||
|
||||
<q-expansion-item
|
||||
v-if="!HIDE_API"
|
||||
v-if="!g.settings.hideApi"
|
||||
group="api"
|
||||
dense
|
||||
expand-separator
|
||||
@@ -273,7 +273,7 @@
|
||||
</q-card>
|
||||
</q-expansion-item>
|
||||
<q-expansion-item
|
||||
v-if="!HIDE_API"
|
||||
v-if="!g.settings.hideApi"
|
||||
group="api"
|
||||
dense
|
||||
expand-separator
|
||||
|
||||
@@ -258,15 +258,20 @@
|
||||
class="q-mb-md"
|
||||
>
|
||||
</q-input>
|
||||
<div v-if="!g.user.email" class="row"></div>
|
||||
<div v-if="!g.user.email" class="row">
|
||||
{% if "google-auth" in LNBITS_AUTH_METHODS or
|
||||
"github-auth" in LNBITS_AUTH_METHODS %}
|
||||
<div class="col q-pa-sm text-h6">
|
||||
<div
|
||||
v-if="
|
||||
'google-auth' in g.settings.authMethods ||
|
||||
'github-auth' in g.settings.authMethods
|
||||
"
|
||||
class="col q-pa-sm text-h6"
|
||||
>
|
||||
<span v-text="$t('verify_email')"></span>:
|
||||
</div>
|
||||
{%endif%} {% if "google-auth" in LNBITS_AUTH_METHODS %}
|
||||
<div class="col q-pa-sm">
|
||||
<div
|
||||
v-if="'google-auth' in g.settings.authMethods"
|
||||
class="col q-pa-sm"
|
||||
>
|
||||
<q-btn
|
||||
:href="`/api/v1/auth/google?user_id=${g.user.id}`"
|
||||
type="a"
|
||||
@@ -284,8 +289,10 @@
|
||||
<div>Google</div>
|
||||
</q-btn>
|
||||
</div>
|
||||
{%endif%} {% if "github-auth" in LNBITS_AUTH_METHODS %}
|
||||
<div class="col q-pa-sm">
|
||||
<div
|
||||
v-if="'github-auth' in g.settings.authMethods"
|
||||
class="col q-pa-sm"
|
||||
>
|
||||
<q-btn
|
||||
:href="`/api/v1/auth/github?user_id=${g.user.id}`"
|
||||
type="a"
|
||||
@@ -303,7 +310,6 @@
|
||||
<div>GitHub</div>
|
||||
</q-btn>
|
||||
</div>
|
||||
{%endif%}
|
||||
</div>
|
||||
</q-card-section>
|
||||
|
||||
@@ -508,7 +514,7 @@
|
||||
<div class="col-8">
|
||||
<lnbits-notifications-btn
|
||||
v-if="g.user"
|
||||
pubkey="{{ WEBPUSH_PUBKEY }}"
|
||||
pubkey="g.settings.webpushPubkey"
|
||||
></lnbits-notifications-btn>
|
||||
</div>
|
||||
</div>
|
||||
@@ -552,7 +558,7 @@
|
||||
></span>
|
||||
<br />
|
||||
<q-badge
|
||||
v-if="!LNBITS_NOSTR_CONFIGURED"
|
||||
v-if="!g.settings.nostrConfigured"
|
||||
v-text="$t('not_connected')"
|
||||
></q-badge>
|
||||
</div>
|
||||
@@ -572,7 +578,7 @@
|
||||
<span v-text="$t('notifications_chat_id')"></span>
|
||||
<br />
|
||||
<q-badge
|
||||
v-if="!LNBITS_TELEGRAM_CONFIGURED"
|
||||
v-if="!g.settings.telegramConfigured"
|
||||
v-text="$t('not_connected')"
|
||||
></q-badge>
|
||||
</div>
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
<div class="home row justify-center items-center">
|
||||
<div
|
||||
class="full-width content-center"
|
||||
:style="`max-width: ${hasCustomImage ? '850' : '600'}px; min-height: 55vh;`"
|
||||
:style="`max-width: ${g.settings.customImage ? '850' : '600'}px; min-height: 55vh;`"
|
||||
>
|
||||
<div v-if="showHomepageElements" class="row q-mb-md">
|
||||
<div v-if="g.settings.showHomepageElements" class="row q-mb-md">
|
||||
<div class="col-12">
|
||||
<div>
|
||||
<h5 v-text="siteTitle" class="q-my-none"></h5>
|
||||
<h5 v-text="g.settings.siteTitle" class="q-my-none"></h5>
|
||||
<template v-if="$q.screen.gt.sm">
|
||||
<h6 class="q-my-sm" v-text="siteTagline"></h6>
|
||||
<h6 class="q-my-sm" v-text="g.settings.siteTagline"></h6>
|
||||
<p class="q-my-sm" v-html="formatDescription"></p>
|
||||
</template>
|
||||
</div>
|
||||
@@ -31,7 +31,10 @@
|
||||
<div class="row">
|
||||
<div
|
||||
class="col-12"
|
||||
:class="{'col-sm-7': hasCustomImage, 'col-lg-6': hasCustomImage}"
|
||||
:class="{
|
||||
'col-sm-7': g.settings.customImage,
|
||||
'col-lg-6': g.settings.customImage
|
||||
}"
|
||||
>
|
||||
<div v-if="showClaimLnurl">
|
||||
<q-card-section>
|
||||
@@ -54,8 +57,8 @@
|
||||
<div v-else>
|
||||
<username-password
|
||||
v-if="authMethod != 'user-id-only'"
|
||||
:allowed_new_users="allowRegister"
|
||||
:auth-methods="LNBITS_AUTH_METHODS"
|
||||
:allowed_new_users="g.settings.allowRegister"
|
||||
:auth-methods="g.settings.authMethods"
|
||||
:auth-action="authAction"
|
||||
v-model:user-name="username"
|
||||
v-model:password_1="password"
|
||||
@@ -70,7 +73,7 @@
|
||||
v-if="authAction !== 'reset'"
|
||||
>
|
||||
<p
|
||||
v-if="authAction === 'login' && allowRegister"
|
||||
v-if="authAction === 'login' && g.settings.allowRegister"
|
||||
class="q-mb-none"
|
||||
>
|
||||
Not registered?
|
||||
@@ -82,7 +85,9 @@
|
||||
>
|
||||
</p>
|
||||
<p
|
||||
v-else-if="authAction === 'login' && !allowRegister"
|
||||
v-else-if="
|
||||
authAction === 'login' && !g.settings.allowRegister
|
||||
"
|
||||
class="q-mb-none"
|
||||
>
|
||||
<span v-text="$t('new_user_not_allowed')"></span>
|
||||
@@ -101,7 +106,7 @@
|
||||
</username-password>
|
||||
<user-id-only
|
||||
v-if="authMethod == 'user-id-only'"
|
||||
:allowed_new_users="allowRegister"
|
||||
:allowed_new_users="g.settings.allowRegister"
|
||||
v-model:usr="usr"
|
||||
v-model:wallet="walletName"
|
||||
:auth-action="authAction"
|
||||
@@ -114,16 +119,20 @@
|
||||
</user-id-only>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="hasCustomImage" class="col-sm-5 col-lg-6 gt-xs">
|
||||
<div v-if="g.settings.customImage" class="col-sm-5 col-lg-6 gt-xs">
|
||||
<div class="full-height flex flex-center q-pa-lg">
|
||||
<q-img :src="hasCustomImage" :ratio="1" width="250px"></q-img>
|
||||
<q-img
|
||||
:src="g.settings.customImage"
|
||||
:ratio="1"
|
||||
width="250px"
|
||||
></q-img>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</q-card>
|
||||
</div>
|
||||
|
||||
<div v-if="showHomepageElements">
|
||||
<div v-if="g.settings.showHomepageElements">
|
||||
<div class="flex flex-center q-gutter-md q-py-md">
|
||||
<q-btn
|
||||
outline
|
||||
@@ -148,10 +157,14 @@
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="adsEnabled"
|
||||
v-if="g.settings.showAdSpace"
|
||||
class="justify-center col-10 q-my-lg row q-col-gutter-sm"
|
||||
>
|
||||
<a :href="ad[0]" v-for="ad in g.ads" class="lnbits-ad col-12 col-md-4">
|
||||
<a
|
||||
:href="ad[0]"
|
||||
v-for="ad in g.settings.adSpace"
|
||||
class="lnbits-ad col-12 col-md-4"
|
||||
>
|
||||
<q-img v-if="$q.dark.isActive" :src="ad[1]"></q-img>
|
||||
<q-img v-else :src="ad[2]"></q-img>
|
||||
</a>
|
||||
|
||||
@@ -172,15 +172,15 @@
|
||||
></q-tooltip>
|
||||
</q-btn>
|
||||
<div
|
||||
v-if="WALLET_FEATURED_BUTTON_URL"
|
||||
v-if="g.settings.walletFeaturedButtonUrl"
|
||||
class="float-right q-mt-sm q-ml-sm"
|
||||
>
|
||||
<q-btn
|
||||
color="primary"
|
||||
:label="WALLET_FEATURED_BUTTON_LABEL"
|
||||
:icon="WALLET_FEATURED_BUTTON_ICON || undefined"
|
||||
:label="g.settings.walletFeaturedButtonLabel"
|
||||
:icon="g.settings.walletFeaturedButtonIcon || undefined"
|
||||
size="sm"
|
||||
:to="WALLET_FEATURED_BUTTON_URL"
|
||||
:to="g.settings.walletFeaturedButtonUrl"
|
||||
>
|
||||
</q-btn>
|
||||
</div>
|
||||
@@ -212,10 +212,10 @@
|
||||
@send-lnurl="handleSendLnurl"
|
||||
:chart-config="chartConfig"
|
||||
></lnbits-wallet-extra>
|
||||
<q-card class="lnbits-wallet-ads" v-if="AD_SPACE_ENABLED">
|
||||
<q-card class="lnbits-wallet-ads" v-if="g.settings.showAdSpace">
|
||||
<q-card-section class="text-subtitle1">
|
||||
<span v-text="AD_SPACE_TITLE"></span>
|
||||
<a :href="ad[0]" class="lnbits-ad" v-for="ad in g.ads">
|
||||
<span v-text="g.settings.adSpaceTitle"></span>
|
||||
<a :href="ad[0]" class="lnbits-ad" v-for="ad in g.settings.adSpace">
|
||||
<q-img class="q-mb-xs" v-if="$q.dark.isActive" :src="ad[1]"></q-img>
|
||||
<q-img class="q-mb-xs" v-else :src="ad[2]"></q-img>
|
||||
</a>
|
||||
@@ -289,7 +289,7 @@
|
||||
></q-input>
|
||||
</div>
|
||||
<q-input
|
||||
v-if="has_holdinvoice"
|
||||
v-if="g.settings.hasHoldinvoice"
|
||||
filled
|
||||
dense
|
||||
v-model="receive.data.payment_hash"
|
||||
|
||||
@@ -45,10 +45,8 @@
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
const WINDOW_SETTINGS = {{ WINDOW_SETTINGS | tojson }}
|
||||
Object.keys(WINDOW_SETTINGS).forEach(key => {
|
||||
window[key] = WINDOW_SETTINGS[key]
|
||||
})
|
||||
const SETTINGS = {{ SETTINGS | tojson | safe }}
|
||||
const CURRENCIES = {{ CURRENCIES | tojson | safe }}
|
||||
</script>
|
||||
{% include('components.vue') %}{% block vue_templates %}{% endblock %} {%
|
||||
for url in INCLUDED_JS %}
|
||||
|
||||
Reference in New Issue
Block a user