chore: code format
This commit is contained in:
@@ -332,9 +332,10 @@ def _add_wasm_extension_frame_route(
|
|||||||
response.headers["Cache-Control"] = "no-store"
|
response.headers["Cache-Control"] = "no-store"
|
||||||
response.headers["Cross-Origin-Opener-Policy"] = "same-origin"
|
response.headers["Cross-Origin-Opener-Policy"] = "same-origin"
|
||||||
response.headers["Cross-Origin-Resource-Policy"] = "same-origin"
|
response.headers["Cross-Origin-Resource-Policy"] = "same-origin"
|
||||||
|
# for access will use the bridge API, but we don't want to allow any other access
|
||||||
response.headers["Permissions-Policy"] = (
|
response.headers["Permissions-Policy"] = (
|
||||||
"camera=(), microphone=(), geolocation=(), payment=(), "
|
"camera=(), microphone=(), geolocation=(), payment=(), "
|
||||||
"clipboard-read=(), clipboard-write=(), usb=()"
|
"clipboard-read=(), usb=()"
|
||||||
)
|
)
|
||||||
response.headers["Referrer-Policy"] = "no-referrer"
|
response.headers["Referrer-Policy"] = "no-referrer"
|
||||||
response.headers["X-Content-Type-Options"] = "nosniff"
|
response.headers["X-Content-Type-Options"] = "nosniff"
|
||||||
|
|||||||
@@ -37,10 +37,7 @@
|
|||||||
><span v-text="extension.name"></span>
|
><span v-text="extension.name"></span>
|
||||||
</q-item-label>
|
</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
<q-item-section
|
<q-item-section side v-show="extensionActive(extension)">
|
||||||
side
|
|
||||||
v-show="extensionActive(extension)"
|
|
||||||
>
|
|
||||||
<q-icon name="chevron_right" color="grey-5" size="md"></q-icon>
|
<q-icon name="chevron_right" color="grey-5" size="md"></q-icon>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
|
|||||||
@@ -1,164 +1,158 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %} {% block styles %}
|
||||||
|
<style>
|
||||||
|
.wasm-extension-frame {
|
||||||
|
border: 0;
|
||||||
|
display: block;
|
||||||
|
height: calc(100vh - 56px);
|
||||||
|
min-height: calc(100vh - 56px);
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
{% endblock %} {% block page_container %}
|
||||||
|
<q-page-container>
|
||||||
|
<q-page class="q-pa-none">
|
||||||
|
<iframe
|
||||||
|
id="lnbits-wasm-extension-frame"
|
||||||
|
class="wasm-extension-frame"
|
||||||
|
src="{{ frame_url }}"
|
||||||
|
title="{{ extension.name }}"
|
||||||
|
sandbox="allow-scripts allow-forms"
|
||||||
|
referrerpolicy="no-referrer"
|
||||||
|
></iframe>
|
||||||
|
</q-page>
|
||||||
|
</q-page-container>
|
||||||
|
{% endblock %} {% block scripts %}
|
||||||
|
<script>
|
||||||
|
;(() => {
|
||||||
|
const bridge = {{ bridge | tojson | safe }}
|
||||||
|
|
||||||
{% block styles %}
|
function extensionFrameWindow() {
|
||||||
<style>
|
return document.getElementById('lnbits-wasm-extension-frame')
|
||||||
.wasm-extension-frame {
|
?.contentWindow
|
||||||
border: 0;
|
|
||||||
display: block;
|
|
||||||
height: calc(100vh - 56px);
|
|
||||||
min-height: calc(100vh - 56px);
|
|
||||||
width: 100%;
|
|
||||||
}
|
}
|
||||||
</style>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block page_container %}
|
function sendResponse(targetWindow, id, payload) {
|
||||||
<q-page-container>
|
targetWindow?.postMessage(
|
||||||
<q-page class="q-pa-none">
|
{
|
||||||
<iframe
|
type: 'lnbits-extension:response',
|
||||||
id="lnbits-wasm-extension-frame"
|
id,
|
||||||
class="wasm-extension-frame"
|
...payload
|
||||||
src="{{ frame_url }}"
|
},
|
||||||
title="{{ extension.name }}"
|
'*'
|
||||||
sandbox="allow-scripts allow-forms"
|
)
|
||||||
referrerpolicy="no-referrer"
|
}
|
||||||
></iframe>
|
|
||||||
</q-page>
|
|
||||||
</q-page-container>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block scripts %}
|
function allowedApiRoute(method, path) {
|
||||||
<script>
|
let url
|
||||||
;(() => {
|
try {
|
||||||
const bridge = {{ bridge | tojson | safe }}
|
url = new URL(path, window.location.origin)
|
||||||
|
} catch (_error) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (url.origin !== window.location.origin) return false
|
||||||
|
|
||||||
function extensionFrameWindow() {
|
method = String(method || 'GET').toUpperCase()
|
||||||
return document.getElementById('lnbits-wasm-extension-frame')
|
return bridge.apiRoutes.some(route => {
|
||||||
?.contentWindow
|
return (
|
||||||
|
route.method === method &&
|
||||||
|
new RegExp(route.pattern).test(url.pathname)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function callApi(message) {
|
||||||
|
const method = String(message.method || 'GET').toUpperCase()
|
||||||
|
const path = String(message.path || '')
|
||||||
|
if (!allowedApiRoute(method, path)) {
|
||||||
|
throw new Error('Extension API route is not allowed.')
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendResponse(targetWindow, id, payload) {
|
const options = {
|
||||||
targetWindow?.postMessage(
|
method,
|
||||||
{
|
headers: {},
|
||||||
type: 'lnbits-extension:response',
|
credentials: 'same-origin'
|
||||||
id,
|
}
|
||||||
...payload
|
if (message.body !== undefined && message.body !== null) {
|
||||||
},
|
options.headers['content-type'] = 'application/json'
|
||||||
'*'
|
options.body = JSON.stringify(message.body)
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(path, options)
|
||||||
|
const text = await response.text()
|
||||||
|
let data = text
|
||||||
|
if (text) {
|
||||||
|
try {
|
||||||
|
data = JSON.parse(text)
|
||||||
|
} catch (_error) {
|
||||||
|
data = text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(
|
||||||
|
typeof data === 'object' && data.detail ? data.detail : text
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
function allowedApiRoute(method, path) {
|
function notify(message) {
|
||||||
let url
|
const level = ['positive', 'negative', 'warning', 'info'].includes(
|
||||||
try {
|
message.level
|
||||||
url = new URL(path, window.location.origin)
|
)
|
||||||
} catch (_error) {
|
? message.level
|
||||||
return false
|
: 'info'
|
||||||
}
|
if (window.Quasar?.Notify) {
|
||||||
if (url.origin !== window.location.origin) return false
|
window.Quasar.Notify.create({
|
||||||
|
color: level,
|
||||||
method = String(method || 'GET').toUpperCase()
|
message: String(message.message || '')
|
||||||
return bridge.apiRoutes.some(route => {
|
|
||||||
return (
|
|
||||||
route.method === method &&
|
|
||||||
new RegExp(route.pattern).test(url.pathname)
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function callApi(message) {
|
window.addEventListener('message', async event => {
|
||||||
const method = String(message.method || 'GET').toUpperCase()
|
if (event.source !== extensionFrameWindow()) return
|
||||||
const path = String(message.path || '')
|
const message = event.data
|
||||||
if (!allowedApiRoute(method, path)) {
|
if (!message || message.type !== 'lnbits-extension:request') return
|
||||||
throw new Error('Extension API route is not allowed.')
|
|
||||||
}
|
|
||||||
|
|
||||||
const options = {
|
try {
|
||||||
method,
|
if (message.action === 'context') {
|
||||||
headers: {},
|
|
||||||
credentials: 'same-origin'
|
|
||||||
}
|
|
||||||
if (message.body !== undefined && message.body !== null) {
|
|
||||||
options.headers['content-type'] = 'application/json'
|
|
||||||
options.body = JSON.stringify(message.body)
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await fetch(path, options)
|
|
||||||
const text = await response.text()
|
|
||||||
let data = text
|
|
||||||
if (text) {
|
|
||||||
try {
|
|
||||||
data = JSON.parse(text)
|
|
||||||
} catch (_error) {
|
|
||||||
data = text
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(
|
|
||||||
typeof data === 'object' && data.detail ? data.detail : text
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
function notify(message) {
|
|
||||||
const level = ['positive', 'negative', 'warning', 'info'].includes(
|
|
||||||
message.level
|
|
||||||
)
|
|
||||||
? message.level
|
|
||||||
: 'info'
|
|
||||||
if (window.Quasar?.Notify) {
|
|
||||||
window.Quasar.Notify.create({
|
|
||||||
color: level,
|
|
||||||
message: String(message.message || '')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener('message', async event => {
|
|
||||||
if (event.source !== extensionFrameWindow()) return
|
|
||||||
const message = event.data
|
|
||||||
if (!message || message.type !== 'lnbits-extension:request') return
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (message.action === 'context') {
|
|
||||||
sendResponse(event.source, message.id, {
|
|
||||||
ok: true,
|
|
||||||
data: {
|
|
||||||
extensionId: bridge.extensionId,
|
|
||||||
public: bridge.public,
|
|
||||||
routeParams: bridge.routeParams,
|
|
||||||
query: bridge.query
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (message.action === 'api') {
|
|
||||||
sendResponse(event.source, message.id, {
|
|
||||||
ok: true,
|
|
||||||
data: await callApi(message)
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (message.action === 'ui.notify') {
|
|
||||||
notify(message)
|
|
||||||
sendResponse(event.source, message.id, {
|
|
||||||
ok: true,
|
|
||||||
data: {ok: true}
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error('Unknown extension bridge action.')
|
|
||||||
} catch (error) {
|
|
||||||
sendResponse(event.source, message.id, {
|
sendResponse(event.source, message.id, {
|
||||||
ok: false,
|
ok: true,
|
||||||
error: error instanceof Error ? error.message : String(error)
|
data: {
|
||||||
|
extensionId: bridge.extensionId,
|
||||||
|
public: bridge.public,
|
||||||
|
routeParams: bridge.routeParams,
|
||||||
|
query: bridge.query
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
return
|
||||||
}
|
}
|
||||||
})
|
|
||||||
})()
|
if (message.action === 'api') {
|
||||||
</script>
|
sendResponse(event.source, message.id, {
|
||||||
|
ok: true,
|
||||||
|
data: await callApi(message)
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.action === 'ui.notify') {
|
||||||
|
notify(message)
|
||||||
|
sendResponse(event.source, message.id, {
|
||||||
|
ok: true,
|
||||||
|
data: {ok: true}
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('Unknown extension bridge action.')
|
||||||
|
} catch (error) {
|
||||||
|
sendResponse(event.source, message.id, {
|
||||||
|
ok: false,
|
||||||
|
error: error instanceof Error ? error.message : String(error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})()
|
||||||
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
Reference in New Issue
Block a user